首页 > 代码库 > SHELL脚本攻略(读书笔记)--1.11 命令替换和子shell的作用

SHELL脚本攻略(读书笔记)--1.11 命令替换和子shell的作用

1.11.1 命令替换

Linux中使用反引号“``”(在波浪线的按键上)或者$()来执行命令替换。一般以$()更直观也更方便敲入。

[root@xuexi tmp]# echo Can you tell me what date it is?  Oh my pleasure $(date +%F)                   

Can you tell me what date it is? Oh my pleasure 2016-09-25

[root@xuexi tmp]# echo Can you tell me what date it is?  Oh my pleasure `date +%F`  ?或者使用反引号

使用$()可以让括号里的命令提前于整个命令运行,然后作为stdin交给整个命令。它可以存在于任何地方。

实际上它的本质是通过()开启了一个子shell,在子shell里运行括号里的命令,运行完退出子shell,然后父shell将结果通过$符号打包,因为这个结果经常交给外部命令,所以不能让结果有换行的行为,所以打包过程中默认将换行符替换为了空格,当然对于只有一行的结果就无所谓了。

下面这个例子展示的就是多行的,可以看到在$打包()的时候去掉了换行符,变为了空格,除了最后一个。

[root@xuexi tmp]# (cat abc.sh) | cat -E

#!/bin/bash$

#文件名:abc.sh$

printf "%-s\t %-s\t %s\n" No Name Mark$

printf "%-s\t %-s\t %4.2f\n" 1 Sarath 80.34$

printf "%-s\t %-s\t %4.2f\n" 2 James 90.998$

printf "%-s\t %-s\t %4.2f\n" 3 Jeff 77.564$

[root@xuexi tmp]# echo $(cat abc.sh) | cat -E

#!/bin/bash #文件名:abc.sh printf "%-s\t %-s\t %s\n" No Name Mark printf "%-s\t %-s\t %4.2f\n" 1 Sarath 80.34 printf "%-s\t %-s\t %4.2f\n" 2 James 90.998 printf "%-s\t %-s\t %4.2f\n" 3 Jeff 77.564$

使用双引号引用可以保留换行符。

[root@xuexi tmp]# echo "$str"

#!/bin/bash

#文件名:abc.sh

printf "%-s\t %-s\t %s\n" No Name Mark

printf "%-s\t %-s\t %4.2f\n" 1 Sarath 80.34

printf "%-s\t %-s\t %4.2f\n" 2 James 90.998

printf "%-s\t %-s\t %4.2f\n" 3 Jeff 77.564

下面的命令确实可以知道括号是开启了子shell的。

[root@xuexi tmp]# ps -ef|grep bash;(ps -ef|grep bash);ps -ef|grep bash            

root       6557   6554  0 08:41 pts/1    00:00:02 -bash

root       7925   6557  0 15:44 pts/1    00:00:00 grep --color=auto bash

root       6557   6554  0 08:41 pts/1    00:00:02 -bash

root       7926   6557  0 15:44 pts/1    00:00:00 -bash    ?shell

root       7928   7926  0 15:44 pts/1    00:00:00 grep --color=auto bash

root       6557   6554  0 08:41 pts/1    00:00:02 -bash

root       7930   6557  0 15:44 pts/1    00:00:00 grep --color=auto bash

1.11.2 关于子shell

子shell会继承父shell的环境变量以及一些其他的变量,但是还有一些是不继承的。在BASH 4以后可以使用$BASH_SUBSHELL变量来查看从当前进程开始的子shell层数

1.11.3 何时产生子shell

1. 运行脚本

当运行脚本里有#!/bin/bash在第一行,就使用这个/bin/bash开启了一个子shell,当然也可以是其他bash。脚本运行完毕回到父shell,所以脚本不会对父shell产生任何环境上的改变。

2. 使用括号()

在()里运行命令也会开启了一个子shell。因此在Linux中不要随便乱写()来想着改变优先级,除非将其转义,并且转以后的括号要与括号里的内容之间有空格。

[root@xuexi tmp]# echo $BASH_SUBSHELL

0

[root@xuexi tmp]# (echo $BASH_SUBSHELL)

1  ?说明开启了一层子shell

但是请看下面的例子。

[root@xuexi tmp]# abc=123

[root@xuexi tmp]# (echo $abc)

123

本应该不继承自定义的变量,但是结果却得到了父shell的结果。这是因为fork一个新的进程会使用父进程的值赋值。所以()这样的操作符打开的子shell会得到父shell的值,但是使用脚本就不能得到了。

fork进程:UNIX关于进程管理的一个术语,本质是新开一个进程,但是不是从磁盘加载代码,而是从内存现有进程复制一份。

3. 使用显式的bash

技术分享

实际上执行脚本就是运行了bash进入子shell,这样的进入方式不会使用父进程的值来赋值。

[root@xuexi tmp]# bash

[root@xuexi tmp]# echo $BASH_SUBSHELL

0  ?0,因为现在$BASH_SUBSHELL以子shell作为计算的父shell

SHELL脚本攻略(读书笔记)--1.11 命令替换和子shell的作用