输出
# ls
和
# echo $(ls)
是一样的。$符号和括号到底是什么意思?
另外,从技术层面上讲发生了什么,导致这两个命令的输出相同?
答案1
这是 bash 的命令替换。
命令替换允许命令的输出替换命令本身。当命令以下列方式括起来时,就会发生命令替换:
$(command) or `command`
http://www.gnu.org/software/bash/manual/bashref.html#Command-Substitution
更多的:
http://tldp.org/LDP/abs/html/commandsub.html
http://wiki.bash-hackers.org/syntax/expansion/cmdsubst
与您的问题类似的一个很好的例子:
http://bashshell.net/shell-scripts/using-command-substitution-in-a-bash-shell-script/
答案2
当 shell 遇到 中包含的文本时$(
)
,它会:
作为heartsmagic 的答案解释道,这是两种可用语法之一为了命令替换。
“什么……导致这两个命令的输出相同?”
实际上,输出结果不太ls
常见确切地与输出相同echo $(ls)
:
ek@Io:~/tmp$ ls
bar foo
ek@Io:~/tmp$ echo $(ls)
bar foo
ls
通常用两个或多个空格或换行符分隔文件名。这有助于我们更容易区分它们,尤其是因为文件名中的空格比较常见(但多个连续的空格不太常见)。
单词拆分
当我跑步的时候echo $(ls)
,发生了以下的事情。
命令替换替换
$(ls)
为:bar foo
听到这个你可能会感到惊讶,因为这可能不是你
ls
单独运行时看到的结果!输出结果的这种差异将在下面解释,但实际上这并不是你最终在这两个单词之间出现一个空格的原因。如果用(有两个空格)替换,ls
也会发生同样的事情。$(ls)
bar foo
随后,shell 执行单词拆分,单独
bar
处理foo
参数到echo
命令中,而不是作为包含空格的单个参数。通配符
*
如果命令的输出包含、?
或[
,那么也会执行(又名文件名/路径名扩展) 。
什么时候echo
接收多个参数(除了像 这样的选项-n
,它们会被特殊处理并且根本不会打印),它会将它们全部打印出来,并在后续参数之间留一个空格。echo
然后打印一个换行符,除非-n
传递了选项。
因此echo $(ls)
显示的输出类似于bar foo
而不是bar foo
。
如果你运行ls
并且它没有列出任何文件,或者只有一个文件,则输出ls
将经常与 的输出相同echo $(ls)
。即便如此,它也不会总是相同,例如当文件名包含除孤立(单个)空格以外的空格时:
ek@Io:~/tmp2$ touch 'my file'
ek@Io:~/tmp2$ ls
my file
ek@Io:~/tmp2$ echo $(ls)
my file
当ls
打印多个文件时,其输出不太可能与的输出完全相同$(ls)
。同样:
echo a b
并echo $(echo a b)
产生相同的输出,但echo 'a b'
也echo $(echo 'a b')
不要。
关于命令替换,有一件很重要的事情需要了解——未加引号的命令替换会受到单词拆分(和通配)的影响。
引用
如果你想预防单词拆分和通配符--而且你通常将要想要阻止它们——您可以将命令替换表达式括在双引号( "
"
)。使用双引号而不是单引号的原因是单引号更强;它们会抑制命令替换。
这也适用于参数扩展,这是一种更常用的shell 扩展而不是命令替换,算术扩展. 引用命令替换很重要的原因(除了在相当不常见的情况之外)是知道您希望进一步扩展)与引用参数扩展的原因。
终端检测
双引号通常足以避免命令替换导致意外结果。特别是,它可以与echo
上面基于的示例以及ls
目录中包含单个包含空格的条目的示例一起使用:
ek@Io:~/tmp2$ echo "$(ls)"
my file
但是,如上所述,您可能会惊讶地发现,运行时看到的内容ls
通常不是传递给的echo
内容"$(ls)"
:
ek@Io:~/tmp$ ls
bar foo
ek@Io:~/tmp$ echo "$(ls)"
bar
foo
这是因为ls
检查标准输出是一个终端决定如何格式化其输出,当没有明确指定输出格式时。
- 当 stdout 是终端时,
ls
以垂直排序列输出,如ls -C
。 - 当 stdout 不是终端时,
ls
将每个条目列在其自己的行上,例如ls -1
。
在命令替换中,标准输出不是终端,因为命令的输出不会直接发送到终端供您查看 - 相反,它被 shell 捕获并用作另一个命令的一部分。
为了在通过命令替换运行时获得多列格式ls
,请向其传递-C
标志:
ek@Io:~/tmp$ echo "$(ls -C)"
bar foo
(有时被建议作为和的dir
替代ls -C
也会起作用尽管如此,dir
表现得像ls -C -b
而不仅仅是ls -C
。
Shell 别名
另一个原因ls
可能有时表现不同,echo "$(ls)"
可能ls
是shell 别名。运行alias ls
检查;在 Ubuntu 上你通常会得到alias ls='ls --color=auto'
。这使得当它ls
作为你以交互方式运行的命令的第一个单词出现时(并且在非交互式 shell 中启用了别名扩展的不太常见的情况下),它会被替换为ls --color=auto
。
例如,当我运行时ls
,它会列出蓝色的目录和绿色的可执行文件(并且也遵守许多其他着色规则)。当标准输出是终端时,--color=auto
会打印彩色输出,否则不会。ls
ls
为了在通过命令替换运行时获得彩色输出,传递给--color
或--color=always
选项。如果您愿意,可以将其与以下内容结合使用-C
:
echo $(ls -C --color)
请注意,虽然你可以为或创建ls
一个别名来代替,并且不关心标准输出是否是终端……当通过命令替换调用时,该别名仍然不会导致产生彩色输出。这是因为 shell 别名(在ls --color
ls --color=always
ls --color=auto
ls --color=always
ls
伯恩风格的贝壳喜欢狂欢) 仅当 shell 将它们视为命令的第一个单词时才会展开,并且它们不会被子 shell 继承。
答案3
()
并$()
用于在子 shell 中运行命令。
答案4
在变量名前使用 $ 会调用赋给变量的值。没有 $ 的 Echo 只会将变量名打印到屏幕(或标准输出)上。