有两种方法可以在bash
中捕获命令行的输出:
旧版Bourne shell反推``
:
var=`command`
$()
语法(据我所知,特定于Bash,或者至少不受原始Bourne等非POSIX旧shell支持)
var=$(command)
与反引号相比,使用第二种语法有什么好处?或两者完全相同?
答案 0 :(得分:133)
主要的一个是能够嵌套它们,命令中的命令,而不会失去理智,试图找出某种形式的转义是否会对反引号起作用。
一个例子,虽然有些做作:
deps=$(find /dir -name $(ls -1tr 201112[0-9][0-9]*.txt | tail -1l) -print)
将为您提供/dir
目录树中所有文件的列表,这些文件与2011年12月(a)中最早的日期文本文件同名。
另一个例子是获取父目录的名称(不是完整路径):
pax> cd /home/pax/xyzzy/plugh
pax> parent=$(basename $(dirname $PWD))
pax> echo $parent
xyzzy
(a)既然特定的命令实际上可能无法正常工作,我还没有测试过这个功能。所以,如果你投票给我,你已经忘记了意图:-)这只是为了说明如何嵌套,而不是作为一个无错误的生产就绪片段。
答案 1 :(得分:48)
假设您要查找与安装gcc
的位置对应的lib目录。你有一个选择:
libdir=$(dirname $(dirname $(which gcc)))/lib
libdir=`dirname \`dirname \\\`which gcc\\\`\``/lib
第一个比第二个更容易 - 使用第一个。
答案 2 :(得分:24)
反引号(`...`
)是最老的非POSIX兼容的bourne-shell所需的遗留语法,$(...)
是POSIX,因为以下几个原因而更受欢迎:
反引号内的反斜杠(\
)以非显而易见的方式处理:
$ echo "`echo \\a`" "$(echo \\a)"
a \a
$ echo "`echo \\\\a`" "$(echo \\\\a)"
\a \\a
# Note that this is true for *single quotes* too!
$ foo=`echo '\\'`; bar=$(echo '\\'); echo "foo is $foo, bar is $bar"
foo is \, bar is \\
$()
内的嵌套引用更方便:
echo "x is $(sed ... <<<"$y")"
而不是:
echo "x is `sed ... <<<\"$y\"`"
或写一些类似的东西:
IPs_inna_string=`awk "/\`cat /etc/myname\`/"'{print $1}' /etc/hosts`
因为$()
使用全新的上下文来引用
这是不可移植的,因为Bourne和Korn shell需要这些反斜杠,而Bash和dash则不需要。
嵌套命令替换的语法更容易:
x=$(grep "$(dirname "$path")" file)
比:
x=`grep "\`dirname \"$path\"\`" file`
因为$()
强制执行一个全新的引用上下文,所以每个命令替换都受到保护,可以单独处理,而不必特别关注引用和转义。当使用反引号时,它会在两级以上的级别后变得更加丑陋和丑陋。
更多例子:
echo `echo `ls`` # INCORRECT
echo `echo \`ls\`` # CORRECT
echo $(echo $(ls)) # CORRECT
它解决了使用反引号时行为不一致的问题:
echo '\$x'
输出\$x
echo `echo '\$x'`
输出$x
echo $(echo '\$x')
输出\$x
反引号语法对嵌入式命令的内容有历史限制,无法处理包含反引号的某些有效脚本,而较新的$()
表单可以处理任何类型的有效嵌入式脚本。
例如,这些其他有效的嵌入式脚本在左栏中不起作用,但在右侧 IEEE 上工作:
echo ` echo $(
cat <<\eof cat <<\eof
a here-doc with ` a here-doc with )
eof eof
` )
echo ` echo $(
echo abc # a comment with ` echo abc # a comment with )
` )
echo ` echo $(
echo '`' echo ')'
` )
因此,$
- 前缀command substitution的语法应该是首选方法,因为它在视觉上清晰,语法清晰(提高了人机和机器的可读性),它是可嵌套的,直观的,内部解析是单独的,并且它也更加一致(所有其他扩展都是从双引号中解析的),其中反引号是唯一的例外,`
字符在与"
相邻时很容易被伪装,使其均匀更难阅读,尤其是小字体或不寻常的字体。
来源:BashFAQ的Why is $(...)
preferred over `...`
(backticks)?
另见:
答案 3 :(得分:21)
来自man bash:
$(command)
or
`command`
Bash performs the expansion by executing command and replacing the com-
mand substitution with the standard output of the command, with any
trailing newlines deleted. Embedded newlines are not deleted, but they
may be removed during word splitting. The command substitution $(cat
file) can be replaced by the equivalent but faster $(< file).
When the old-style backquote form of substitution is used, backslash
retains its literal meaning except when followed by $, `, or \. The
first backquote not preceded by a backslash terminates the command sub-
stitution. When using the $(command) form, all characters between the
parentheses make up the command; none are treated specially.
答案 4 :(得分:9)
除了其他答案,
$(...)
在视觉上优于
`...`
Backticks看起来太像撇号;这取决于您使用的字体。
(而且,正如我刚刚注意到的,反引号在内联代码示例中输入更加困难。)
答案 5 :(得分:6)
$()
允许嵌套。
out=$(echo today is $(date))
我认为反引号不允许。
答案 6 :(得分:3)
这是一个遗留问题,但我提出了$(...)
超过`...`
的完美有效示例。
我正在使用远程桌面来运行cygwin的Windows,并希望迭代命令的结果。可悲的是,由于远程桌面设备或cygwin本身,无法进入反击角色。
假设美元符号和括号在这种奇怪的设置中更容易输入,这是明智的。
答案 7 :(得分:2)
POSIX标准定义了$(command)
形式的命令替换。今天使用的大多数shell都符合POSIX标准,并且在古老的反引号符号上支持这种首选形式。 Shell语言文档的command substitution部分(2.6.3)描述了这一点:
命令替换允许替换命令的输出来代替命令名称本身。命令替换将在命令括起时发生,如下所示:
$(command)
或(反引号):
`command`
shell应通过执行命令扩展命令替换 在子shell环境中(参见Shell Execution Environment)和 替换命令替换(命令的文本加上 将“$()”或反引号括起来与标准输出 命令,删除一个或多个
<newline>
个字符的序列 替换结束。在结束之前嵌入<newline>
个字符 输出不得删除;但是,他们可能被视为 字段分隔符并在字段拆分期间消除,具体取决于 IFS的值和有效的引用。如果输出包含 任何空字节,行为都是未指定的。在反引号的命令替换方式中,
<backslash>
应该 保留其字面含义,除非后跟:“$
”,“`
”或<backslash>
。应满足对匹配反引号的搜索 由第一个未引用的非逃避反引号;在此搜索期间,如果是 在shell注释中遇到非转义的反引号,a here-document,$(命令)的嵌入式命令替换 表单或带引号的字符串,发生未定义的结果。单引号或 双引号字符串,在“`...`
”内开始但不结束 序列产生不确定的结果。使用$(命令)表单,打开后的所有字符 括号到匹配的右括号构成 命令。除了a之外,任何有效的shell脚本都可用于命令 脚本仅由重定向组成,产生未指定的 结果
命令替换的结果不得进一步处理 代字扩展,参数扩展,命令替换或 算术扩展。如果在里面发生命令替换 双引号,字段拆分和路径名扩展不得 根据替代结果进行。
命令替换可以嵌套。指定嵌套 反引号,应用程序应在内部反引号之前
<backslash>
个字符;例如:
\`command\`
shell命令语言的语法对于以“
$((
”开头的扩展有歧义, 它可以引入以子shell开头的算术扩展或命令替换。 算术扩展优先;也就是说,shell应首先确定 是否可以将扩展解析为算术扩展 并且只能将扩展解析为命令替换 如果它确定它不能将扩展解析为算术扩展。 执行此确定时,shell无需评估嵌套扩展。 如果它在没有确定的情况下遇到输入结束 它不能将扩展解析为算术扩展, shell应将扩展视为不完整的算术扩展并报告语法错误。 符合要求的申请应确保将“$(
”和“(
”分成两个令牌 (也就是说,用空格分隔它们)在以子shell开头的命令替换中。 例如,包含单个子shell的命令替换可以写为:
$( (command) )
答案 8 :(得分:0)
在 2021 年,值得一提的是一个奇怪的事实,作为对其他答案的补充。
用于管道的 Microsoft DevOps YAML“脚本”可能包括 bash tasks。但是,符号 $()
用于引用在 YAML 上下文中定义的变量,因此在这种情况下,应使用反引号来捕获命令的输出。
当将脚本代码复制到 YAML 脚本时,这主要是一个问题,因为 DevOps 预处理器对不存在的变量非常宽容,因此不会有错误消息。