第五章 理解shell
1.shell的类型
系统启动什么样的shell程序取决于你的人的用户ID配置。在/etc/passwd
文件中,用户ID记录的第7个字段中列出了默认的shell程序,只要用户登录到某个虚拟机控制台终端或是在GUI中启动终端仿真器,默认的shell程序就会开始运行。
$ cat /etc/passwd
[...]
Christine:x:501:501:Christine B:/home/Christine:/bin/bash
上图配置的是bash
,其他的shell程序还有tcsh(CentOS)
,dash(Debian)
。这些shell都可以被设置为用户默认的shell。
2.shell的父子关系
用户登录某个虚拟机控制器终端或在GUI中运行终端仿真器时所启动的默认的交互shell,是一个父shell
(下图PID1841的shell)。在CLI提示符后输出/bin/bash
或bash
命令时(CLI内不会有任何显示),会创建一个新的shell程序。这个shell程序被称为子shell(child shell)
。子shell(PID2430的shell)也拥有CLI提示符,同样会等待命令输入。
$ ps -f
UID PID PPID C STIME TTY TIME CMD
501 1841 1840 0 11:50 pts/0 00:00:00 -bash
501 2429 1841 4 13:44 pts/0 00:00:00 ps -f
$
$ bash
$
$ ps -f
UID PID PPID C STIME TTY TIME CMD
501 1841 1840 0 11:50 pts/0 00:00:00 -bash
501 2430 1841 0 13:44 pts/0 00:00:00 bash
501 2444 2430 1 13:44 pts/0 00:00:00 ps -f
exit
命令可以退出子shell,还能用来登出当前的虚拟机控制台终端或终端仿真器软件,只需要在父shell中输入exit
,就能够退出CLI。
进程列表
命令列表:允许你在一行中指定要依次运行的一系列命令,只需要在命令之间加入;
即可。
$ pwd ; ls ; cd /etc ; pwd ; cd ; pwd ; ls
/home/Christine
Desktop Downloads Music Public Videos
Documents junk.dat Pictures Templates
/etc
/home/Christine
Desktop Downloads Music Public Videos
Documents junk.dat Pictures Templates
进程列表:将命令列表放在括号中就成为了进程列表。
$ (pwd ; ls ; cd /etc ; pwd ; cd ; pwd ; ls)
/home/Christine
Desktop Downloads Music Public Videos
Documents junk.dat Pictures Templates
/etc
/home/Christine
Desktop Downloads Music Public Videos
Documents junk.dat Pictures Templates
括号的加入使命令列表变成了进程列表,生成了一个子shell来执行对应的命令。可以查看环境变量(BASH_SUBSHELL,第六章介绍)来确定是否生成了子进程。使用命令echo $BASH_SUBSHELL
,如果返回0表示没有子shell,返回≥1表明存在子shell。
$ pwd ; echo $BASH_SUBSHELL
/home/Christine
0
$ (pwd ; echo $BASH_SUBSHELL)
/home/Christine
1
可以在命令列表中嵌套括号来创建子shell的子shell。
$ ( pwd ; echo $BASH_SUBSHELL)
/home/Christine
1
$ ( pwd ; (echo $BASH_SUBSHELL))
/home/Christine
2
在shell脚本中,经常使用子shell进行多进程处理。
有趣的shell用法
后台模式:可以在运行命令的同时让出CLI,以供他用。sleep
命令接受一个参数,表示希望进程等待(休眠)的秒数。如果要想命令置入后台模式,可以在命令末尾加上字符&
。
$ sleep 3000&
[1] 2396
$ ps -f
UID PID PPID C STIME TTY TIME CMD
christi+ 2338 2337 0 10:13 pts/9 00:00:00 -bash
christi+ 2396 2338 0 10:17 pts/9 00:00:00 sleep 3000
christi+ 2397 2338 0 10:17 pts/9 00:00:00 ps -f
sleep
命令会在后台(&
)休眠3000秒。shell CLI提示符返回之前,会出现两条信息,后台作业号(background job)1,后台进程ID(2396)。
jobs
命令可以显示当前运行在后台模式中的所有用户的进程(作业)信息。
$ jobs
[1]+ Running sleep 3000 &
jobs
显示了作业号(1),当前状态(running),对应命令(sleep 3000 &)。
后台模式非常方便,可以在CLI中创建有意义的子shell。
将进程列表置入后台
可以将进程列表置入后台模式,允许你在子shell中进行耗时的工作,同时也不会让子shell的I/O受制于终端。
$ (sleep 2 ; echo $BASH_SUBSHELL ; sleep 2)&
[2] 2401
$ 1
[2]+ Done ( sleep 2; echo $BASH_SUBSHELL; sleep 2 )
$
协程
协程可以同时做两件事,在后台生成一个子shell,并在这个子shell中执行命令。命令为coproc
。
$ coproc sleep 10
[1] 2544
除了会创建子shell外,协程将命令置入后台模式。
$ jobs
[1]+ Running coproc COPROC sleep 10 &
可以使用命令的拓展语法自定义协程名称。(第一个{
和命令之前必须有一个空格,以;
结尾,;
和}
之间也必须有一个空格)
$ coproc My_Job { sleep 10; }
[1] 2570
$
$ jobs
[1]+ Running coproc My_Job { sleep 10; } &
3.理解shell的内建命令
外部命令
也称为文件系统命令
,是存在于bash shell之外的程序。外部命令并不是shell程序的一部分,外部命令程序通常位于/bin
,/usr/bin
,/sbin
或/usr/sbin
中。
ps
就是一个外部命令,可以使用which
和type
命令查看命令程序的位置。
$ which ps
/bin/ps
$
$ type -a ps
ps is /bin/ps
$
$ ls -l /bin/ps
-rwxr-xr-x 1 root root 93232 Jan 6 18:32 /bin/ps
当外部命令执行时,会创建一个子进程。这种操作叫做衍生(forking)
。
$ ps -f
UID PID PPID C STIME TTY TIME CMD
christi+ 2743 2742 0 17:09 pts/9 00:00:00 -bash
christi+ 2801 2743 0 17:16 pts/9 00:00:00 ps -f
内建命令
与外部命令区别在于不需要使用子进程来执行。内建命令已经和shell编译成了一体,作为shell工具的组成部分存在,不需要借助外部程序文件来运行。
$ type cd
cd is a shell builtin
$
$ type exit
exit is a shell builtin
因为既不需要通过衍生出子进程来执行,也不需要打开程序文件,内建命令的执行速度快,效率更高。有些命令有多种实现,例如echo
和pwd
既有内建命令也有外部命令。可以通过type -a
查看。
$ type -a echo
echo is a shell builtin
echo is /bin/echo
$
$ which echo
/bin/echo
$
$ type -a pwd
pwd is a shell builtin
pwd is /bin/pwd
$
$ which pwd
/bin/pwd
type -a
命令显示了每个命令的两种实现,which
命令只显示了外部命令文件。
一些有用的内建命令
history
:查看最近用过的命令列表。通常历史记录会保存最近的1000条命令。命令历史记录被保存在隐藏文件.bash_history
中,文件位于用户的主目录中。bash命令的历史记录是先存放在内存中,当shell退出时才被写入到历史文件中。可以在退出shell会话之前强制命令历史记录写入到.bash_history
文件中,使用history -a
命令。可以唤回历史列表中任意一条命令,只需要输入!
和命令在历史列表中的编号即可。
$ history
19 which echo
20 type -a pwd
21 type -a echo
$
$ !20
type -a pwd
pwd is a shell builtin
pwd is /bin/pwd
alias
:命令别名允许你为常用的命令(以及参数)创建另一个名称,从而将输入量减少到最低。可以使用alias -p
查看当前可用的别名。
$ alias -p
[...]
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias l='ls -CF'
可以使用alias命令创建属于自己的别名(常用,提升工作效率)。
$ alias li='ls -li'
$
$ li
total 36
529581 drwxr-xr-x. 2 Christine Christine 4096 May 19 18:17 Desktop
在定义好别名之后,可以随时在shell中使用,也可以在shell脚本中使用。命令别名属于内部命令,一个别名仅在它所被定义的shell进程中有效。(可以修改shell的配置文件.bashrc
对所有shell起作用)
本章小结
1.介绍了shell的类型
2.父进程与子进程
3.shell的内建命令和外部命令
参考文献:Linux命令行与shell脚本编程大全(第三版)Richard Blum Christine Bresnahan著 门佳 武海峰译