“ls”和“echo $(ls)”之间的区别

“ls”和“echo $(ls)”之间的区别

考虑两个外壳样本

$ 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?被扩展两次,导致文件名file1file2在输出中出现两次。正如杰菲金斯指出的那样,这是因为,路径名扩展首先由 shell 在ls运行之前执行,然后在echo运行之前再次执行。

第二路径名扩展如果我们使用双引号可以抑制:

$ echo "$(ls file?)"
file?
file1
file2

答案2

ls知道它是否正在将输出发送到终端。

在命令提示符下执行ls将写入您的伪终端。ls通常重定向 will 的输出不是将进入伪终端,并将ls以不同的方式格式化输出。

相关内容