I'm trying to build a command that pipes the results of one grep command to another grep command in a bash function. Ultimately, I want the command executed to look like this:
grep -I -r FooBar /code/internal/dev/ /code/public/dev/ | grep .c:\|.h:
The function I'm writing stores the first part of the command in a string, then appends the second part:
grep_cmd="grep -I -r $pattern $@"
if (( ${#file_types[@]} > 0 )); then
file_types="${file_types[@]}"
file_types=.${file_types// /':\|.'}:
grep_cmd="$grep_cmd | grep $file_types"
fi
echo "$grep_cmd"
${grep_cmd}
This throws an error after the output from the first part:
grep: |: No such file or directory
grep: grep: No such file or directory
grep: .c:\|.h:: No such file or directory
Changing the last line from ${grep_cmd}
to just "$grep_cmd"
displays no output from the first part and throws a different error:
bash: grep -I -r FooBar /code/internal/dev/ /code/public/dev/ | grep .c:\|.h:: No such file or directory
Per this SO answer, I tried changing the last line to $(grep_cmd)
. This throws another error:
bash: grep_cmd: command not found
This SO answer suggests using eval $grep_cmd
. This suppresses the errors but also suppresses the output.
This one suggests using eval ${grep_cmd}
. This has the same results (suppresses the errors and output). I tried enabling debugging in bash (with set -x
), which gives me this:
+ eval grep -I -r FooBar /code/internal/dev/ /code/public/dev/ '|' grep '.c:\|.h:'
++ grep -I -r FooBar /code/internal/dev/ /code/public/dev/
++ grep '.c:|.h:'
It looks like the pipe is being escaped, so the shell interprets the command as two commands. How do I properly escape the pipe character so it interprets it as one command?
Best Answer
As mentioned in the comment, a lot of your difficulty is because you're trying to store a command in a variable, and then run that command later.
You'll have a lot better luck if you just run the command immediately instead of trying to save it.
For example, this should do what you're trying to accomplish: