在 Unix Shell 中, 命令替换(Command Substitution) 是一个非常强大的概念,它被用来将某一个命令的输出作为参数直接插入到另一个命令中,如下例中,我将 date 命令的值赋于 *$today$ 变量,然后再将其输出:

cox@Cox:~$ today=$(date)
cox@Cox:~$ echo "$today"
2012年 11月 21日 星期三 01:39:31 CST

在上面的示例中,我们可以把两人行命令简化为一行:

cox@Cox:~$ echo $(date)
2012年 11月 21日 星期三 01:57:20 CST

或者像下面这样更复杂的使用:

cox@Cox:~$ echo "今天是 $(date +%A),现在时间是 $(date +%H:%M)"
今天是 星期三,现在时间是 01:59

这个示例中, echo 命令调用了 date 命令两个,第一次打印出星期几,第二次打印出当前时间,上面出现的三个命令的顺序是:

第一个 date ⇒ 第二个 date ⇒ echo

当然了,上面只是示例,其实我们有更简单的办法来打印出同样的信息:

cox@Cox:~$ date "+今天是 %A,现在时间是 %H:%M"
今天是 星期三,现在时间是 02:02

但是有一个问题需要注意,嵌套命令的结果会被进行 单词分割,除非所有的内容都被包裹在一个 双引号 中,否则这会导致有些时候你得到的并不是你自己想的,比如下面这行命令:

cox@Cox:~$ ls | grep Ubuntu
Ubuntu One
cox@Cox:~$ for i in $(ls | grep Ubuntu); do echo $i; done
Ubuntu
One

看到他们的不同了没有?我的当前目录下面本身是有一个名为 Ubuntu One 的目录,但是第二个命令本身的意途是想遍历当前目录中所有名称包含 Ubuntu 的文件,但是 Ubuntu One 却被当作两个来处理了,这是因为这个目录中有一个空格的存在,如前面所说的,结果会被进行单词分割,空格使得这本身是一个目录的名称被分割为两个了,对于这个问题,应该使用下面这样的命令去实现:

cox@Cox:~$ for i in Ubuntu*; do echo $i; done
Ubuntu One

命令是可以无限制嵌套的,比如下面这个示例:

cox@Cox:~$ echo $(echo (这是第一层嵌套($(echo 这是第二层嵌套))))
(这是第一层嵌套(这是第二层嵌套))

替换命令可以创建 Subshell ,这很有用,因为 subshell 所作的任何修改都只对它本身有效,不会污染程序的全局环境,比如下面这个示例:

cox@Cox:~$ var=$(cd ../../usr/bin/; pwd)
cox@Cox:~$ echo "$var"
/usr/bin
cox@Cox:~$ pwd
/home/cox

在命令替换中,父命令会去除子命令的输出结果中所有的换行:

cox@Cox:~$ ls -lh | grep Ubuntu
drwxrwxr-x 7 cox  cox      4.0K 11月 20 19:16 Ubuntu One
drwxrwxr-x 2 cox  cox      4.0K 11月 21 02:20 Ubuntu Two
cox@Cox:~$ echo $(ls -lh | grep Ubuntu)
drwxrwxr-x 7 cox cox 4.0K 11月 20 19:16 Ubuntu One drwxrwxr-x 2 cox cox 4.0K 11月 21 02:20 Ubuntu Two

通用性

$(command) 命令可以在 KornShellBASH以及PosixShell 中使用,老一些的 shell (比如BourneShell)则需要使用返引号"",形式是 *command*。

标签: none

评论已关闭