I'm trying to run a command with a pipe but receive errors:
echo abc | `echo "grep a | grep b"`
grep: |: No such file or directory
grep: grep: No such file or directory
grep: b: No such file or directory
What is wrong w开发者_如何学Cith the code?
Expected results is for the following command to be executed:
echo abc | grep a | grep b
With a result of
abc
It's not clear what you are trying to do, but here is what you are doing:
echo "grep a | grep b"
outputs the string grep a | grep b
.
This is the output from the backticks. You are using the backticks in a position where the shell wants a command, so "grep 'a' '|' 'grep' 'b'" is attempted as the command line, with all the tokens interpreted literally (I added single quotes to make this a bit clearer, hopefully) so the shell ignores the input from echo
and instead attempts to look for the regular expression a
in the named files. You apparently have no files named "|", "grep", or "b", so you get an error message.
What you might want is
echo abc | grep a | grep b
which searches for "a" in the output from "echo", then searches for "b" in the output from the first grep. Because abc
matches the regular expression a
, the first grep
succeeds, so the matching line is printed; and because it also matches the regular expression b
it is printed by the final grep
as well.
To expand a bit on this, try the following:
sh$ echo abc; echo bcd; echo xya
abc
bcd
xya
sh$ ( echo abc; echo bcd; echo xya ) | grep a # print lines matching a
abc
xya
sh$ (echo abc; echo bcd; echo xya ) | grep a | grep b # print lines matching a and b
abc
It is not clear why you are using backticks; if this is not what you are trying to achieve, please explain in more detail what you want to accomplish.
If you want to find lines matching either a or b, try this:
sh$ ( echo abc; echo bcd; ) | grep '[ab]'
The pipe character is special only if you let the bash parser see it. You quoted it by using "", so it is no longer special, and grep
thinks you want it to grep the string a
from the files |
, grep
and b
.
In general, once you escape a special character that is part of the shell syntax, you can't unescape it later (because then there would be no way of dealing with a file that is actually called |
- not a good idea, but it is possible, and bash shouldn't be unable to deal with it.)
The only way to get such a character treated as special again is to resubmit it to an entirely new invocation of the shell interpreter, which is what Robert Martin's solution does.
Triplee's explanation is pretty close (if not saying exactly the same thing) to what I think is happening. "Clasic Shell Scripting" [Robbins & Beebe], section 7.8.1, actually have a good explanation of a gotcha which is related to my original question.
If you write
listpage="ls | more"
$listpage
then you get a similar problem where '|' & 'more' are treated as arguments to ls. The reason given is that at the point where variable substitution is done by the shell (step 5/10), the original input has already been broken up into tokens (of which | is one) (step 1/10). So '|' loses its special meaning and is treated as an argument.
However, if you write:
eval $listpage
eval causes the shell to restart back at step 1, where the '|' character is treated as a special token.
This part is wrong:
$ `echo "grep a | grep b"`
Because the backticks don't understand the pipe. Try this alternate that does the same thing:
$ echo "grep a | grep b" | sh
精彩评论