0

I have a shell script that includes this search:

find . -type f -exec grep -iPho "barh(li|mar|ag)" {}  \;

I want to capture each string the grep command finds and send it a function I will create named "parser"

parser(){ 
# do stuff with each single grep result found
}

how can that be done? is this right?

find . -type f -exec grep -iPho "barh(li|mar|ag)" {parser $1}  \;

I do not want to output the entire find command result to the function

3 Answers 3

1

Only shell can execute a function. You need to use bash -c in your find in order to execute it. That is also the reason you need to export your function, so that the new process sees it.

parser() { 
    while IFS= read -r line; do
        echo "Processing line: $line"
    done <<< "$1"
}

export -f parser
find . -type f -exec bash -c 'parser "$(grep -iPho "barh(li|mar|ag)" "$1")"' -- {} \;

The code above will send all occurrences from file1, then file2 etc to your function to process. It will not send each line one by one and therefore you need to loop over the lines in your function. If there is no occurrence of your regex in a file, it will still call your function with an empty input!

That might not be the best solution for you so let's try to add the loop inside the bash -c statement and really process the lines one by one:

parser() { 
    echo "Processing line: $1"
}

export -f parser
find . -type f -exec bash -c 'grep -iPho "barh(li|mar|ag)" "$@" | while IFS= read -r line; do parser "$line"; done' -- {} +

EDIT: Very nice and simple solution not using bash -c suggested by @gniourf_gniourf:

parser() { 
   echo "Processing line: $1"
}

find . -type f -exec grep -iPho "barh(li|mar|ag)" {} + | while IFS= read -r line; do parser "$line"; done

This approach works fine and it will process each line one by one. You also do not need to export your function with this approach. But you have to care for some things that might surprise you.

Each command in a pipeline is executed in its own subshell, and any variable assignment in your parser function or your while in general will be lost after returning from that very subshell. If you are writing a script, simple shopt -s lastpipe will suffice and run the last pipe command in the current shell environment. Or you can use process substitution:

parser() {
   echo "Processing line: $1"
}

while IFS= read -r line; do 
   parser "$line"; 
done < <(find . -type f -exec grep -iPho "barh(li|mar|ag)" {} +)

Note that in the previous bash -c examples, you will experience the same behavior and your variable assignments will be lost as well.

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

4 Comments

Re. the while read loop: if that's what OP wants, then find ... -exec grep ... {} + | while read line; do parser "$line"; done is a good suggestion.
@gniourf_gniourf Thanks for the suggestion! I added it to the answer, if you do not mind.
this worked for me: find . -type f -exec grep -iPho "barh(li|mar|ag)" {} \; | while IFS= read -r line; do parser "$line"; done ... notice the \; instead of the '+'... marked as answer. thanks.
@john_black {} + is more efficient.
0

You need to export your function.

You also need to call bash to execute the function.

parser() {
  echo "GOT: $1"
}
export -f parser

find Projects/ -type f -name '*rb' -exec bash -c 'parser "$0"' {} \;

2 Comments

note that i'm talking about the "grep" command, not just the "find". I tried this: find . -type f -exec grep -iPho "barh(li|mar|ag)" -exec bash -c 'parser "$0"' {} \; and it gave me an error
Sure. But the 2 bits missing are the bash -c and the export ;-)
0

i suggest you to use sed ,this is more powerful tool to do text processing. for example i want to add string "myparse" after the line that end as "ha",i can do this like

# echo "haha" > text1
# echo "hehe" > text2
# echo "heha" > text3
# find . -type f -exec sed '/ha$/s/ha$/ha myparse/' {} \;
haha myparse
heha myparse
hehe

if you really want to replace the file ,not just print to stdout,you can do this like

# find . -type f -exec sed -i '/ha$/s/ha$/ha myparse/' {} \;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.