PERL中对SHELL命令的调用细节分析
######################################################################
######################################################################
举个例子:
# cat test.pl
system("cd");
print $?,"/n";
print $!,"/n";
# perl test.pl
-1
No such file or directory
#
######################################################################
2. Shell如何执行命令 请点
2.1. 执行交互式命令 请点评
用户在命令行输入命令后,一般情况下Shell会fork
并exec
该命令,但是Shell的内建命令例外,执行内建命令相当于调用Shell进程中的一个函数,并不创建新的进程。以前学过的cd
、alias
、umask
、exit
等命令即是内建命令,凡是用which
命令查不到程序文件所在位置的命令都是内建命令,内建命令没有单独的man手册,要在man手册中查看内建命令,应该
$ man bash-builtins
本节会介绍很多内建命令,如export
、shift
、if
、eval
、[
、for
、while
等等。内建命令虽然不创建新的进程,但也会有Exit Status,通常也用0表示成功非零表示失败,虽然内建命令不创建新的进程,但执行结束后也会有一个状态码,也可以用特殊变量$?
读出。
习题 请点评
1、在完成第 5 节 “练习:实现简单的Shell”时也许有的读者已经试过了,在自己实现的Shell中不能执行cd
命令,因为cd
是一个内建命令,没有程序文件,不能用exec
执行。现在请完善该程序,实现cd
命令的功能,用chdir(2)
函数可以改变进程的当前工作目录。
2、思考一下,为什么cd
命令要实现成内建命令?可不可以实现一个独立的cd
程序,例如/bin/cd
,就像/bin/ls
一样?
2.2. 执行脚本 请点评
首先编写一个简单的脚本,保存为script.sh
:
例 31.1. 简单的Shell脚本
#! /bin/sh cd .. ls
Shell脚本中用#
表示注释,相当于C语言的//
注释。但如果#
位于第一行开头,并且是#!
(称为Shebang)则例外,它表示该脚本使用后面指定的解释器/bin/sh
解释执行。如果把这个脚本文件加上可执行权限然后执行:
$ chmod +x script.sh $ ./script.sh
Shell会fork
一个子进程并调用exec
执行./script.sh
这个程序,exec
系统调用应该把子进程的代码段替换成./script.sh
程序的代码段,并从它的_start
开始执行。然而script.sh
是个文本文件,根本没有代码段和_start
函数,怎么办呢?其实exec
还有另外一种机制,如果要执行的是一个文本文件,并且第一行用Shebang指定了解释器,则用解释器程序的代码段替换当前进程,并且从解释器的_start
开始执行,而这个文本文件被当作命令行参数传给解释器。因此,执行上述脚本相当于执行程序
$ /bin/sh ./script.sh
以这种方式执行不需要script.sh
文件具有可执行权限。再举个例子,比如某个sed
脚本的文件名是script
,它的开头是
#! /bin/sed -f
执行./script
相当于执行程序
$ /bin/sed -f ./script.sh
以上介绍了两种执行Shell脚本的方法:
$ ./script.sh $ sh ./script.sh
这两种方法本质上是一样的,执行上述脚本的步骤为:
图 31.1. Shell脚本的执行过程
-
交互Shell(
bash
)fork
/exec
一个子Shell(sh
)用于执行脚本,父进程bash
等待子进程sh
终止。 -
sh
读取脚本中的cd ..
命令,调用相应的函数执行内建命令,改变当前工作目录为上一级目录。 -
sh
读取脚本中的ls
命令,fork
/exec
这个程序,列出当前工作目录下的文件,sh
等待ls
终止。 -
ls
终止后,sh
继续执行,读到脚本文件末尾,sh
终止。 -
sh
终止后,bash
继续执行,打印提示符等待用户输入。
如果将命令行下输入的命令用()括号括起来,那么也会fork
出一个子Shell执行小括号中的命令,一行中可以输入由分号;隔开的多个命令,比如:
$ (cd ..;ls -l)
和上面两种方法执行Shell脚本的效果是相同的,cd ..
命令改变的是子Shell的PWD
,而不会影响到交互式Shell。然而命令
$ cd ..;ls -l
则有不同的效果,cd ..
命令是直接在交互式Shell下执行的,改变交互式Shell的PWD
,然而这种方式相当于这样执行Shell脚本:
$ source ./script.sh
或者
$ . ./script.sh
source
或者.
命令是Shell的内建命令,这种方式也不会创建子Shell,而是直接在交互式Shell下逐行执行脚本中的命令。
再看Unix下面exec这个函数: 摘自APUE
8.10. exec Functions
We mentioned in Section 8.3 that one use of the fork function is to create a new process (the child) that then causes another program to be executed by calling one of the exec functions. When a process calls one of the exec functions, that process is completely replaced by the new program, and the new program starts executing at its main function. The process ID does not change across an exec, because a new process is not created; exec merely replaces the current processits text, data, heap, and stack segmentswith a brand new program from disk.
There are six different exec functions, but we'll often simply refer to "the exec function," which means that we could use any of the six functions. These six functions round out the UNIX System process control primitives. With fork, we can create new processes; and with the exec functions, we can initiate new programs. The exit function and the wait functions handle termination and waiting for termination. These are the only process control primitives we need. We'll use these primitives in later sections to build additional functions, such as popen and system.
#include <unistd.h>
int execl(const char *pathname, const char *arg0,
... /* (char *)0 */ );
int execv(const char *pathname, char *const argv []);
int execle(const char *pathname, const char *arg0, ...
/* (char *)0, char *const envp[] */ );
int execve(const char *pathname, char *const
argv[], char *const envp []);
int execlp(const char *filename, const char *arg0,
... /* (char *)0 */ );
int execvp(const char *filename, char *const argv []);
All six return: 1 on error, no return on success
The first difference in these functions is that the first four take a pathname argument, whereas the last two take a filename argument. When a filename argument is specified
If filename contains a slash, it is taken as a pathname.
Otherwise, the executable file is searched for in the directories specified by the PATH environment variable.
system("sh ./first");
exit;