Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
975 views
in Technique[技术] by (71.8m points)

bash - Execute command in a variable don't execute the latter part of a pipe

I have found that the command expanded from variable won't run the latter parts in a pipe. For example, as following test.sh:

#!/bin/bash

y='echo hello man | awk "{print $1}"'
$y
eval $y

y='echo hello'
$y

The output of the script is:

hello man | awk "{print $1}"
hello
hello

The first $y only execute echo hello man but not execute awk "{print $1}" of the pipe. That's why?
My bash version is 4.3.48.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

That's because the variable is expanded after the pipes and redirection is already done. So in that case | is just another argument to echo, not a pipe that's interpreted by the shell.

Recommended reading: http://mywiki.wooledge.org/BashFAQ/050

When executing the command

echo hello man | awk '{print $1}'

the shell will see the | and setup a pipeline, on one side it will run the command echo hello man and on the other awk '{print $1}'. The commands then get subjected to word splitting so you run the command echo with 2 arguments: hello and man. On the other side you run the command awk with a single argument (because of the quoting) "{print $1}"

When that command is stored as a string though the shell first looks at the command $y and sees no redirection to do. It then expands $y and then does word splitting on it. It gets expanded to the same looking string, but now it's too late for redirection. So it gets split into the words echo, hello, man, |, awk, "{print, $1}" (note that the argument to awk now gets split because the quotes inside the string are part of the string, not syntactical)

The first word in that list is echo so that's the command, and all the rest of the words are passed as arguments to it, which is why you see the output

hello man | awk "{print $1}"

When you do the eval line, it takes that same string and tells bash to parse it as though it had been typed so the pipe once again becomes syntatical and causes the pipeline.

Because echo puts its arguments on the same line it's a little tougher sometimes to see what's happening, if we replace it with printf '%s ' each argument will become its own line though:

$ y='printf %s
 hello man | awk "{print $1}"'
$ $y
hello
man
|
awk
"{print
$1}"

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...