shell 与 命令的执行


在 shell 里执行的命令 3 种:

1.内置命令(Builtin)

shell 执行这些命令时不会派生新进程,而是由 shell 直接执行。比如 read, set, export 都是内置命令,这些命令需要用 help command 来查看其帮助信息。

2. 外部命令

外部命令就是普通的可执行二进制文件,shell 在执行它们时会 fork 出新进程(这是一个子 shell),然后用 exec 系列函数来执行它们,这时候子 shell 的环境就被命令的环境所取代。

3. shell 脚本

在执行 shell 脚本时,shell 同样会先执行 fork 派生出子进程,然后使用 exec 来调用脚本解释程序(内核中会检查脚本中的第一行 #!/bin/xxx 来确定是调用哪一种),然后将脚本装入,由它来解释执行。脚本解释器有很多,比如 bash, cshell, perl, python 等。如果被调出来的解释程序和当前 shell 是同一种 shell,那么它就是当前 shell 的子 shell,脚本中的命令都在子 shell 中执行,不会影响父 shell 的环境。

( ) 和 { } 中的指令组:

在 ( ) 和 { } 中都可以内置一组指令。( ) 中的指令会在一个子 shell 中执行,命令执行结果不影响当前 shell。需要注意的是,$$ 代表当前 shell 进程的 PID,而不是子 shell 进程的 PID 。

{ } 中的指令在当前 shell 中执行,指令执行结果会影响当前的环境。

后台执行和异步执行

在一个 shell 脚本中将一个命令通过 & 放入后台执行,这个命令和当前 shell 的执行是并行的,当前 shell 会派生一个子 shell 执行这个后台命令,而自己则继续往下执行,两者并没有相互依赖及等待的关系,所以这是一种异步的执行方式。以下代码可以说明这一点:

#!/bin/bash

LOG=$0.log

COMMAND1="sleep 100"

echo "Logging PIDs background commands for script: $0" >> "$LOG"

echo >> "$LOG"

echo -n "PID of \"$COMMAND1\": " >> "$LOG"

${COMMAND1} &

echo $! >> "$LOG"

上面的脚本中,sleep 100 这条指令会被放入后台执行,而当前 shell 会继续执行下面的 echo 语句,它并不会等待 sleep 执行完毕后才去执行下面的 echo ,这一点可以通过查看 $0.log 文件得以验证。

命令替换

command 会将 command 命令的输出结果代换到当前的命令行。command 在子 shell 中执行,它的结果不会影响到当前 shell 。比较下面代码:

#!/bin/bash

pwd

dir=`cd /tmp; pwd`

echo $dir

pwd

输出:

$ ./substitute.sh
/home/beyes/shell
/tmp
/home/beyes/shell

当前 shell 所在的目录在并没有改变。

管道

对于 bash 来说(dash,ash 等大部分 shell 也一样),管道中的命令都是放在子 shell 里执行的,所以像以下命令不会得到你希望的结果:

command | read var

管道中,左侧命令的标准输出会作为右侧命令的标准输入。但是上面命令中的 command 的执行结果会被 var 捕获么?看下面例子:

#!/bin/bash

a="hello world"

echo "$a"| read var

echo $var

exit 0

运行输出为空。本来希望 "hello world" 字串被 var 捕获,但是实际上不会。这是因为,管道中的命令是放在子 shell 里执行的,所以 var 得到的值无法传递到当前 shell ,所以这里要输出为空。

那么我们改变一下上面的代码以证明这一点:

#!/bin/bash

a="hello world"

echo "$a"| (read var; echo "In subshell:$var")

echo "In parent shell: $var"


exit 0

运行输出:

beyes@debian:~/shell$ ./var.sh
In subshell:hello world
In parent shell:

好,这时可以正确输出了。如上面所讲,括号中的命令组都属于同一个子 shell,所以括号里面的那个 $var 是属于子 shell,它在括号里输入,当然能看到正常的字串。而在括号之外的 $var,是属于父 shell 的,所以无法得到字串,子 shell 里的变量其实就和 C 语言中的局部变量一样。

但是如 korn shell 可以实现 command |read var 的,这缘于 shell 的设计不同,因为在 korn shell 里管道中的命令是在当前 shell 里执行的。为了保持脚本的兼容性,应该避免这种做法。


转载自:

shell 与 命令的执行


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值