曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 14754|回复: 0
打印 上一主题 下一主题

shell 与 命令的执行

[复制链接]

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34387
跳转到指定楼层
楼主
发表于 2011-6-6 01:24:37 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
在 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 执行这个后台命令,而自己则继续往下执行,两者并没有相互依赖及等待的关系,所以这是一种异步的执行方式。以下代码可以说明这一点:
[Bash 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 。比较下面代码:
[Bash 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 捕获么?看下面例子:
[Bash shell] 纯文本查看 复制代码
#!/bin/bash

a="hello world"

echo "$a"| read var

echo $var

exit 0

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

那么我们改变一下上面的代码以证明这一点:
[Bash 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 里执行的。为了保持脚本的兼容性,应该避免这种做法。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2024-6-17 01:28 , Processed in 0.063174 second(s), 23 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表