0

I'm trying to loop over each line of output from a grep and do something conditionally based on an interactive prompt.

I've looked at these: Iterating over each line of ls -l output

bash: nested interactive read within a loop that's also using read

Does bash support doing a read nested within a read loop?

But I'm still having trouble achieving what I want. I think the key difference is most of those examples are reading input from a file - I want to use a command. I've tried many variations of the following:

#! /bin/bash                                                                        

function test1()                                                                    
{                                                                                   
    local out=$(grep -n -a --color=always "target" file.txt)                                             
    while read -u 3 line; do                                                        
        echo -e "$line"                                                             
        read -r -p "ok? " response                                                  
        echo "you said: $response"                                                  
        if [[ "$response" == 'n' ]]; then                                           
            return                                                                  
        fi                                                                          
    done 3<&0 <<< $out                                                              
}                                                                                   

function test2()                                                                    
{                                                                                   
    grep -n -a --color=always "target" file.txt | while read line; do                                    
        echo -e "$line"                                                             
        read -r -p "ok? " response                                                  
        echo "you said: $response"                                                  
        if [[ "$response" == 'n' ]]; then                                           
            return                                                                  
        fi                                                                          
    done
 }  

In test1() the FD redirection doesn't seem to do what I want. test2() seems to just (unsurprisingly) stomp through my second read. I assume this is because I need to switch up the FDs in use. I've tried to combine the FD redirection from test1 into test2, but I haven't been able to get that to work with the pipe. I'm using bash version 4.4. What am I missing?

Here is an example run for the two functions above:

[~]$ cat file.txt
ksdjfklj target alsdfjaksjf alskdfj asdf asddd

alsdfjlkasjfklasj
asdf
asdfasdfs
target
assjlsadlkfjakls target
target aldkfjalsjdf
[~]$
[~]$ test1
you said: 1:ksdjfklj target alsdfjaksjf alskdfj asdf asddd 6:target 7:assjlsadlkfjakls target 8:target aldkfjalsjdf
you said:
you said:
you said:
you said:
^C
[~]$
[~]$
[~]$ test2
1:ksdjfklj target alsdfjaksjf alskdfj asdf asddd
you said: 6:target
7:assjlsadlkfjakls target
you said: 8:target aldkfjalsjdf
[~]$
awm129
  • 305
  • 2
  • 11

3 Answers3

2

done 3<&0 <<< $out is quite torturous. How about a process substitution instead?

test1() {
    while IFS= read -r -u 3 line; do
        printf '%s' "$line"
        read -r -p "ok? " response
        echo "you said: $response"
        [[ "$response" == [nN]* ]] && return
    done 3< <(
        grep -n -a --color=always "target" file.txt
    )
}

IFS= read -r is the idiom to grab the line exactly verbatim.

In bash you can use funcname() or function funcname but you don't need both the keyword and the parentheses.

glenn jackman
  • 238,783
  • 38
  • 220
  • 352
1

Reading from /dev/tty should work too

test2() {
    grep -n -a --color=always "target" file.txt | \
    while IFS= read -r line; do
        printf '%s' "$line"
        read -r -p "ok? " response </dev/tty
        echo "you said: $response"
        [[ "$response" == [nN]* ]] && return
    done
}
Diego Torres Milano
  • 65,697
  • 9
  • 111
  • 134
  • This is interesting! The only potential downside I can see is I wouldn't be able to use `yes` or something to automate the second `read`. I like not having to use FD shenanigans. – awm129 Feb 07 '19 at 04:01
0

You can try

for line in $(grep ...); do
    echo "$line"
    read ...
    # the rest as in your functions
done

P.S. sorry for such a bad formatting, I will update it when I get to my computer

neshkeev
  • 6,280
  • 3
  • 26
  • 47
  • thanks! The trouble with `for` is it iterates over each space delimited token as opposed to each line. I could possibly solve this by setting `IFS` to something other than ' ' but I think that would mess up my second read. – awm129 Feb 07 '19 at 00:09