开发者

Use of pipes in backticks

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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜