我有一个 bash 脚本,它会根据一些参数在字符串中构建命令行,然后一次性执行它。连接到命令字符串的部分应该用管道分隔,以方便数据在每个组件中“流动”。
一个非常简单的例子:
#!/bin/bash
part1=gzip -c
part2=some_other_command
cmd="cat infile"
if [ ! "$part1" = "" ]
then
cmd+=" | $part1"
fi
if [ ! "$part2" = "" ]
then
cmd+=" | $part2"
fi
cmd+="> outfile"
#show command. It looks ok
echo $cmd
#run the command. fails with pipes
$cmd
由于某种原因,管道似乎不起作用。当我运行此脚本时,我收到不同的错误消息,这些错误消息通常与命令的第一部分(第一个管道之前)有关。
所以我的问题是是否可以通过这种方式构建命令,以及最好的方法是什么?
答案1
这完全取决于何时进行求值。当您输入 时$cmd
,整行的其余部分将作为参数传递给 中的第一个单词$cmd
。
walt@spong:~(0)$ a="cat /etc/passwd"
walt@spong:~(0)$ b="| wc -l"
walt@spong:~(0)$ c="$a $b"
walt@spong:~(0)$ echo $c
cat /etc/passwd | wc -l
walt@spong:~(0)$ $c
cat: invalid option -- 'l'
Try 'cat --help' for more information.
walt@spong:~(1)$ eval $c
62
walt@spong:~(0)$ a="echo /etc/passwd"
walt@spong:~(0)$ c="$a $b"
walt@spong:~(0)$ echo $c
echo /etc/passwd | wc -l
walt@spong:~(0)$ $c
/etc/passwd | wc -l
walt@spong:~(0)$ $c |od -bc
0000000 057 145 164 143 057 160 141 163 163 167 144 040 174 040 167 143
/ e t c / p a s s w d | w c
0000020 040 055 154 012
- l \n
0000024
walt@spong:~(0)$ eval $c
1
这表明传递给命令的参数echo
是:“ /etc/passwd
”,“ |
”(竖线字符),“ wc
”和“ -l
”。
从man bash
:
eval [arg ...]
The args are read and concatenated together into
a single command. This command is then read and
executed by the shell, and its exit status is returned
as the value of eval. If there are no args, or only null
arguments, eval returns 0.
答案2
为方便以后参考,解决此问题的一个方法是使用“eval”。这可确保 bash 会忘记字符串以何种方式进行解释,并读取整个字符串,就像直接在 shell 中输入一样(这正是我们想要的)。
因此在上面的例子中,替换
$cmd
和
eval $cmd
解决了。
答案3
@waltinator 已经解释了为什么这不能像你预期的那样工作。另一种解决方法是使用bash -c
来执行你的命令:
$ comm="cat /etc/passwd"
$ comm+="| wc -l"
$ $comm
cat: invalid option -- 'l'
Try 'cat --help' for more information.
$ bash -c "$comm"
51
答案4
可能更好的方法是避免使用eval
而只使用 Bash 数组及其内联扩展来构建所有参数,然后根据命令执行它们。
runcmd=() # This is slightly messier than declare -a but works
for cmd in $part1 $part2 $part3; do runcmd+="| $cmd "; done
cat infile ${runcmd[@]} # You might be able to do $basecmd ${runcmd[@]}
# but that sometimes requires an `eval` which isn't great