为什么以下脚本按预期工作(打印hello
)
#!/bin/bash
foo=$(bash -c 'echo hello')
echo $foo
而这个脚本:
#!/bin/bash
cmd="bash -c 'echo hello'"
foo=$($cmd)
echo $foo
给出以下错误:
hello': -c: line 0: unexpected EOF while looking for matching `''
hello': -c: line 1: syntax error: unexpected end of file
答案1
在,
cmd="bash -c 'echo hello'"
$cmd
您运行的不是bash -c 'echo hello'
命令,而是$cmd
简单的命令。
不加引号$cmd
意味着调用 split+glob 运算符。这里,使用默认值$IFS
, 的内容$cmd
被拆分为bash
、-c
、'echo
和hello'
。因此,您正在bash
使用这 4 个参数运行,就好像您输入了:
bash -c "'echo" "hello'"
该'echo
代码缺少结束引号(hello'
参数进入$0
该内联脚本的)。
$cmd
如果你想评估shell 代码的内容,那就是
eval "$cmd"
所以:
cmd="bash -c 'echo hello'"
foo=$(eval "$cmd")
echo "$foo"
尽管您也可以以不同的方式使用 split+glob 运算符:
cmd='bash,-c,echo hello'
IFS=, # split on comma
set -f # disable glob
foo=$($cmd)
echo "$foo"
答案2
让我们看一下里面正在执行什么$($cmd)
:
$ cmd="bash -c 'echo hello'"
$ foo=$(printf '<%s>' $cmd)
$ echo "$foo"
<bash> <-c> <'echo> <hello'>
可以看到,执行的命令行是:
$ foo=$( "bash" "-c" "'echo" "hello'")
表达式的一侧有一个单引号:"'echo"
,它被解析为要执行的命令,并且它(正确地)报告缺少bash -c
结束单引号。`''
一种解决方案是使用 eval 促进命令的正确解析:
$ foo=$(eval "printf '<%s>' $cmd"); echo "$foo"
<bash><-c><echo hello>
这有效:
$ foo=$(eval "$cmd"); echo "$foo"
hello
但正确的想法是:“变量存储数据,函数存储代码”。
$ cmd(){ bash -c 'echo hello'; }
$ foo=$(cmd); echo "$foo"
hello