运行此命令时:
ls
终端显示的输出ls
。
运行此命令时:
echo $(ls)
shell捕获的输出$(ls)
并对其进行分词。使用default IFS
,这意味着所有空格序列(包括换行符)都将被单个空格替换。这就是为什么输出echo $(ls)
显示在一行上的原因。
有关分词的高级讨论,请参阅Greg的FAQ。
抑制分词
外壳程序不对引号中的字符串执行单词拆分。因此,您可以使用以下命令抑制字分割并保留多行输出:
echo "$(ls)"
ls
和多行输出
您可能已经注意到,ls
有时每行打印多个文件:
$ ls
file1 file2 file3 file4 file5 file6
当输出ls
到终端时,这是默认设置。当输出不直接发送到终端时,ls
将其默认值更改为每行一个文件:
$ echo "$(ls)"
file1
file2
file3
file4
file5
file6
在中记录了此行为man ls
。
另一个微妙之处:命令替换和尾随换行符
$(...)
是命令替换,shell从命令替换输出中删除尾随换行符。这通常不会引起注意,因为默认情况下,echo
会在其输出末尾添加一个换行符。因此,如果您从末尾丢失了一个换行符,$(...)
而从中获得了一个换行符echo
,则没有任何变化。但是,如果命令的输出以2个或多个换行符结尾而echo
仅加回一个,则您的输出将丢失一个或多个换行符。例如,我们可以printf
用来生成尾随换行符。请注意,尽管换行数不同,以下两个命令仍会产生相同的空白行输出:
$ echo "$(printf "\n")"
$ echo "$(printf "\n\n\n\n\n")"
$
在中记录了此行为man bash
。
另一个惊喜:路径名扩展了两次
让我们创建三个文件:
$ touch 'file?' file1 file2
观察之间的差异ls file?
和echo $(ls file?)
:
$ ls file?
file? file1 file2
$ echo $(ls file?)
file? file1 file2 file1 file2
在的情况下echo $(ls file?)
,文件glob file?
会扩展两次,从而导致文件名file1
并file2
在输出中出现两次。正如Jeffiekins所指出的那样,这是因为路径名扩展首先在ls
运行之前由Shell 执行,然后在echo
运行之前再次执行。
如果我们使用双引号,则可以抑制第二个路径名扩展:
$ echo "$(ls file?)"
file?
file1
file2