bash之命令替换(command substitution)

写在前面

bash在做完参数扩展(请参阅《bash之参数扩展》)之后,紧接着需要做的是命令替换(command substitution)。

命令替换相对其它扩展来说要简单一些,这篇博文就来简单了解下什么是shell的命令替换功能以及应该注意的事项。

 

什么是命令替换

我们来看bash手册里是怎么说的:

Command substitution allows the output of a command to replace the command itself.

再看看Wikpedia是怎么说的:

In computing, command substitution is a facility that allows a command to be run and its output to be pasted back on the command line as arguments to another command. 

OK, 很清晰了,所谓命令替换就是:子命令先运行,最后获取这个子命令的标准输出(stdout)用来重组命令行

 

命令替换的两种形式

命令替换有两种形式,如下:

$( COMMANDS )
` COMMANDS `

废话不多说,先来看两个例子吧。

(1)获取上个星期天的日期。

[16:57:44@astrol:~]$ echo `date -d "last sunday" +%Y-%m-%d`
2018-12-16
[16:58:09@astrol:~]$ echo $(date -d "last sunday" +%Y-%m-%d)
2018-12-16

(2)列出/etc目录下以ren结尾的文件。

[17:09:47@astrol:/etc]$ for f in $(ls /etc/*.ren); do echo "$f"; done
/etc/hosts.ren
/etc/nsswitch.conf.ren
/etc/ntp.conf.ren
/etc/rsyslog.conf.ren

通过这两个例子可以发现,使用$(COMMAND)或者`COMMAND`可以让COMMAND提前整个命令运行,最后将COMMAND的标准输出(stdout)内容插入到COMMAND符号处。

 

命令替换的两个特点

Bash performs the expansion by executing COMMAND in a subshell environment and replacing the command substitution with the standard output of the command, (1)with any trailing newlines deleted.  Embedded newlines are not deleted, but they may be removed during word splitting. (2)The command substitution '$(cat FILE)' can be replaced by the equivalent but faster '$(< FILE)'.

通过阅读bash手册中这一小段,我们可以总结出命令替换具备两个特点。

特点一: bash会将子命令标准输出的最后换行符全部删除掉

我们通过以下例子来增加感性认识:

[10:01:21@astrol:~]$ echo -n -e "a\nb\nc\n\n\n"
a
b
c


[10:01:25@astrol:~]$ echo -n -e "a\nb\nc\n\n\n" | hexdump -C
00000000  61 0a 62 0a 63 0a 0a 0a                           |a.b.c...|
00000008
[10:01:26@astrol:~]$ echo -n -e $(echo -n -e "a\nb\nc\n\n\n")
a b c[10:02:12@astrol:~]$ echo -n -e $(echo -n -e "a\nb\nc\n\n\n") | hexdump -C
00000000  61 20 62 20 63                                    |a b c|
00000005
[10:03:51@astrol:~]$ echo -n -e "$(echo -n -e "a\nb\nc\n\n\n")"
a
b
c[10:04:27@astrol:~]$ echo -n -e "$(echo -n -e "a\nb\nc\n\n\n")" | hexdump -C
00000000  61 0a 62 0a 63                                    |a.b.c|
00000005
[10:04:31@astrol:~]$

通过观察echo -n -e $(echo -n -e "a\nb\nc\n\n\n")的结果可以发现,命令替换的确将最后的三个“\n”吃掉了,中间的两个“\n”是由于后续的单词分割(word splitting)根据变量IFS的值替换成了空格(0x20)。那么通过双引号括起来为什么中间的两个“\n”就保留了呢? 回答这个问题请看下图:

可以看到,当用双引号扩起来之后,bash跳过了单词分割(word splitting)和路径扩展(pathname expansion)这两项扩展,自然就保留了中间的两个“\n”。其实在bash手册的最后也给出了解释:

If the substitution appears within double quotes, word splitting and filename expansion are not performed on the results.

那命令替换为什么默认会删除最后的换行符?是处于什么考虑吗?

这里可以大胆地猜测下:因为命令替换的结果经常交给外部命令,不应该让结果有换行的行为,所以默认将最后的换行符删除掉

 

特点二  '$(cat FILE)'在命令替换中更有效的形式是'$(< FILE)'

如果我们通过命令替换仅仅是想获得某个文件的内容,那么我们可以使用$(<FILE)的形式,这种形式比使用$(cat FILE)更有效的原因是不需要再创建一个子shell去运行cat命令。

 

两种形式的比较

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 substitution.  When using the '$(COMMAND)' form, all characters between the parentheses make up the command; none are treated specially.

过多的细节就不去研究啦,只要记住一点:尽量使用$(COMMAND)这种形式,`COMMAND`形式最大的缺点就是在嵌套使用命令替换时不方便,需要使用反斜杠进行转换,非常麻烦。

echo `echo `ls``      # INCORRECT
echo `echo \`ls\``    # CORRECT
echo $(echo $(ls))    # CORRECT

运行上面三条命令,你会发现第一条命令输出的是“ls”,为什么?

这是因为shell自动把前两个`和后两个`当成了命令替换,因为这前后两个命令替换的结果都为空,所以最后只剩“ls”了。

 

好了,本文结束~

参考链接:

Command substitution

shell的命令替换和命令组合

What is the benefit of using $() instead of backticks in shell scripts?

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值