考虑两个外壳样本
$ ls
myDoc.html
SomeDirectory
someDoc.txt
和
$ echo $(ls)
myDoc.html SomeDirectory someDoc.txt
据我了解,第一个执行的操作ls
是将当前工作目录的内容附加到文件中stdout
(这是终端显示的内容)。它是否正确?
第二个获取命令的值ls
(这意味着当前工作目录的内容)并将其打印到文件中stdout
。它是否正确?
为什么这两个命令给出不同的输出?
答案1
当您运行此命令时:
ls
终端显示 的输出ls
。
当您运行此命令时:
echo $(ls)
shell 捕获输出$(ls)
并执行分词在上面。使用默认值IFS
,这意味着所有空白序列,包括换行符被单个空格替换。这就是为什么 的输出echo $(ls)
出现在一行上的原因。
有关分词的高级讨论,请参阅格雷格常见问题及解答。
抑制分词
shell 不会对引号中的字符串执行分词。因此,您可以通过以下方式抑制分词并保留多行输出:
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?)
,文件 globfile?
被扩展两次,导致文件名file1
和file2
在输出中出现两次。正如杰菲金斯指出的那样,这是因为,路径名扩展首先由 shell 在ls
运行之前执行,然后在echo
运行之前再次执行。
第二路径名扩展如果我们使用双引号可以抑制:
$ echo "$(ls file?)"
file?
file1
file2
答案2
ls
知道它是否正在将输出发送到终端。
在命令提示符下执行ls
将写入您的伪终端。ls
通常重定向 will 的输出不是将进入伪终端,并将ls
以不同的方式格式化输出。