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
[~]$
1
  • 1
    It would be great if you show us some input and output. Commented Feb 7, 2019 at 0:35

3 Answers 3

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.

Sign up to request clarification or add additional context in comments.

1 Comment

There is something about a herestring into a redirected stdin that just doesn't quite sit right...
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
}

1 Comment

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.
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

1 Comment

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.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.