当用于eval
命令时,是否eval
将 shell 两次应用于命令的重定向部分?
假设重定向中的文件名包含空格,如果eval
多次对其应用所有解析、shell 扩展和分词等步骤,则在第二轮分词步骤中,文件名将被拆分为两个单词。
以下示例是否意味着eval
不会对以下命令的重定向部分应用两次 shell,因此文件名不会拆分为两个单词?
$ filename="my file"
$ eval "cat" < "$filename"
hi
答案1
在您提供的示例中,唯一的事情eval
是cat
,重定向发生在外部,eval
并且文件作为命令的标准输入提供eval "cat"
。
一种变体是使用单引号引用整个命令,包括重定向:
$ eval 'cat < "$filename"'
hi
现在eval
正在获取整个字符串,包括重定向和变量名,因此它正在执行变量扩展以及所需的带空格的文件名引用。这仍然有效。
另一种选择是对字符串使用双引号:
$ eval "cat < '$filename'"
hi
现在,该变量由 shell 扩展,但这仍然有效,因为其中的引号将文件名保持在一起。 (请注意,如果文件名包含撇号,这会中断。)
什么“行不通”是这样的:
$ eval "cat" "<" "$filename"
这与您的示例类似,但是使用引号<
,外部 shell 不会执行重定向。eval
然后将参数放在一起,生成的命令将是:
cat < my file
这不会按预期工作,因为周围的引号my file
现在已经消失了......
答案2
其工作方式eval
是将参数一起构造为一个 shell 命令来运行。由于你的变量filename
被引用,不会进行分词什么时候这命令被构建为运行。
同样的情况可能不适用于文件名带有空格的情况,并且不带引号的扩展将导致错误。例如
filname=my\ file
echo "dude" > my\ file
eval cat < $filename
bash: $filename: ambiguous redirect
请注意此处完成的分词,并cat
收到用于输入重定向的不正确文件名,从而导致错误。
另请注意,如果使用时不进行重定向cat
,如果直接在文件上使用,在这种情况下eval
将打开两个文件,即cat
eval cat $filename
将转化为跑步
cat my file
例如,如果当前目录中有指定的文件,它将显示其内容并显示错误流上file
指定的文件不存在。my
filename=my\ file
echo "foo" > file
我们正在使文件my
不存在
eval cat $filename 2>&1
cat: my: No such file or directory
foo