1. shell 类型
只要用户登录到某个虚拟控制台终端或是在 GUI 中启动终端仿真器,默认的 shell 程序就会开始运行。
默认的交互 shell 会在用户登录某个虚拟控制台终端或在 GUI 中运行终端仿真器时启动。不过还有另外一个默认 shell 是 /bin/sh,它作为默认的系统 shell,用于那些需要在启动时使用的系统 shell脚本。
$ cat /etc/passwd
[...]
christine:x:1000:1000:Christine,,,:/home/christine:/bin/bash
$
$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Apr 22 12:33 /bin/sh -> dash
可以看到,用户 christine 默认的交互 shell 是 /bin/bash,也就是 bash shell。但是作为默认系统shell 的 /bin/sh 被设置为 dash shell。
2. shell 的父子关系
用于登录某个虚拟控制器终端或在 GUI 中运行终端仿真器时所启动的默认的交互 shell,是一个父shell。父 shell 提供 CLI 提示符,然后等待命令输入。
在 CLI 提示符后输入 /bin/bash 命令或其他等效的 bash 命令时,会创建一个新的 shell 程序。这个shell 程序被称为子 shell(child shell)。子 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
注意:进程就是正在运行的程序。bash shell 是一个程序,当它运行的时候,就成为了一个进程。一个运行着的 shell 就是某种进程而已。
子 shell 的父进程 ID(PPID)是 1841,指明了这个父 shell 进程就是该子 shell 的父进程。
子 shell(child shell,也叫 subshell)可以从父 shell 中创建,也可以从另一个子 shell 中创建。
$ ps -f
UID PID PPID C STIME TTY TIME CMD
501 1841 1840 0 11:50 pts/0 00:00:00 -bash
501 2532 1841 1 14:22 pts/0 00:00:00 ps -f
$
$ bash
$
$ bash
$
$ bash
$
$ ps --forest
PID TTY TIME CMD
1841 pts/0 00:00:00 bash
2533 pts/0 00:00:00 \_ bash
2546 pts/0 00:00:00 \_ bash
2562 pts/0 00:00:00 \_ bash
2576 pts/0 00:00:00 \_ ps
bash 命令被输入了三次。这实际上创建了三个子 shell。ps -forest 命令展示了这些子 shell 间的嵌套结构。
可以利用 exit 命令有条不紊地退出子 shell。exit 命令不仅能退出子 shell,还能用来登出当前的虚拟控制台终端或终端仿真器软件。只需要在父 shell 中输入 exit,就能够从容退出 CLI 了。
2.1 进程列表
在一行中输入多个命令,只需要在命令之间加入分号 ; 即可:
$ 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 脚本中,经常使用子 shell 进行多进程处理。但是采用子 shell 的成本不菲,会明显拖慢处理速度。在交互式的 CLI shell 会话中,子 shell 同样存在问题。它并非真正的多进程处理,因为终端控制着子 shell 的 I/O。
2.2 子 shell 的其它用法
后台模式: 在后台模式中运行命令可以在处理命令的同时让出 CLI,以供他用,要想将命令置入后台模式,可以在命令末尾加上字符 &
$ 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
$
jobs 命令可以显示出当前运行在后台模式中的所有用户的进程(作业)。
$ jobs
[1]+ Running sleep 3000 &
$
jobs 命令在方括号中显示出作业号(1)。它还显示了作业的当前状态(running)以及对应的命令(sleep 3000 &)
利用 jobs 命令的 -l(字母L的小写形式)选项还能够显示出命令的 PID
$ jobs -l
[1]+ 2396 Running sleep 3000 &
$
[1]+ Done sleep 3000 &
$
生成子shell的成本不低,而且速度还慢。创建嵌套子shell更是火上浇油!
3. 理解 shell 的内建命令
3.1 外部命令
外部命令: 有时候也被称为文件系统命令,是存在于 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 很方便显示出它的父进程以及自己所对应的衍生子进程。
$ 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
$
3.2 内建命令
内建命令和外部命令的区别在于前者不需要使用子进程来执行。它们已经和 shell 编译成了一体,作为 shell 工具的组成部分存在。不需要借助外部程序文件来运行。
cd 和 exit 命令都内建于 bash shell。可以利用 type 命令来了解某个命令是否是内建的。
$ type cd
cd is a shell builtin
$
$ type exit
exit is a shell builtin
$
因为既不需要通过衍生出子进程来执行,也不需要打开程序文件,内建命令的执行速度要更快,效率也更高。
要查看命令的不同实现,使用 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命令只显示出了外部命令文件。