在学习GNU bash shell期间,你可能听到过“内建命令”这个术语。搞明白shell的内建命令
和非内建(外部)命令非常重要。内建命令和非内建命令的操作方式大不相同。5.3.1 外部命令
5.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
$
作为外部命令,ps命令执行时会创建出一个子进程。在这里,ps命令的PID是2801,父PID
是2743。作为父进程的bash shell的PID是2743。图5-3展示了外部命令执行时的衍生过程。
当进程必须执行衍生操作时,它需要花费时间和精力来设置新子进程的环境。所以说,外部
命令多少还是有代价的。
说明 就算衍生出子进程或是创建了子shell,你仍然可以通过发送信号与其沟通,这一点无论是
在命令行还是在脚本编写中都是极其有用的。发送信号(signaling)使得进程间可以通过
信号进行通信。信号及其发送会在第16章中讲到。
5.3.2 内建命令
内建命令和外部命令的区别在于前者不需要使用子进程来执行。它们已经和shell编译成了一
体,作为shell工具的组成部分存在。不需要借助外部程序文件来运行。
cd和exit命令都内建于bash shell。可以利用type命令来了解某个命令是否是内建的。
$ type cd
cd is a shell builtin
$
$ type exit
exit is a shell builtin
$
因为既不需要通过衍生出子进程来执行,也不需要打开程序文件,内建命令的执行速度要更
快,效率也更高。附录A给出了GNU bash shell的内建命令列表。
要注意,有些命令有多种实现。例如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命令只显示出了外部命令文件。
窍门 对于有多种实现的命令,如果想要使用其外部命令实现,直接指明对应的文件就可以了。
例如,要使用外部命令pwd,可以输入/bin/pwd。
1. 使用history命令
一个有用的内建命令是history命令。bash shell会跟踪你用过的命令。你可以唤回这些命令
并重新使用。
要查看最近用过的命令列表,可以输入不带选项的history命令。
$ history
1 ps -f
2 pwd
3 ls
4 coproc ( sleep 10; sleep 2 )
5 jobs
6 ps —forest
7 ls
8 ps -f
9 pwd
10 ls -l /bin/ps
11 history
12 cd /etc
13 pwd
14 ls
15 cd
16 type pwd
17 which pwd
18 type echo
19 which echo
20 type -a pwd
21 type -a echo
22 pwd
23 history
在这个例子中,只显示了最近的23条命令。通常历史记录中会保存最近的1000条命令。这个
数量可是不少的!
窍门 你可以设置保存在bash历史记录中的命令数。要想实现这一点,你需要修改名为HISTSIZE的环境变量(参见第6章)。
你可以唤回并重用历史列表中最近的命令。这样能够节省时间和击键量。输入!!,然后按回
车键就能够唤出刚刚用过的那条命令来使用。
$ ps —forest
PID TTY TIME CMD
2089 pts/0 00:00:00 bash
2744 pts/0 00:00:00 _ ps
$
$ !!
ps —forest
PID TTY TIME CMD
2089 pts/0 00:00:00 bash
2745 pts/0 00:00:00 _ ps
$
当输入!!时,bash首先会显示出从shell的历史记录中唤回的命令。然后执行该命令。
命令历史记录被保存在隐藏文件.bash_history中,它位于用户的主目录中。这里要注意的是,
bash命令的历史记录是先存放在内存中,当shell退出时才被写入到历史文件中。
$ history
[…]
25 ps —forest
26 history
27 ps —forest
28 history
$
$ cat .bash_history
pwd
ls
history
exit
$
注意,当history命令运行时,列出了28条命令。出于简洁性的考虑,上面的例子中只摘取
了一部分列表内容。但是文件.bash_history的内容被显示出来时,其中只有4条命令,与history
命令的输出并不匹配。
可以在退出shell会话之前强制将命令历史记录写入.bash_history文件。要实现强制写入,需
要使用history命令的-a选项。
$ history -a
$
$ history
[…]
25 ps —forest
26 history
27 ps —forest
28 history
29 ls -a
30 cat .bash_history
31 history -a
32 history
$
$ cat .bash_history
[…]
ps —forest
history
ps —forest
history
ls -a
cat .bash_history
history -a
由于两处输出内容都太长,因此都做了删减。注意,history命令和.bash_history文件的输
入是一样的,除了最近的那条history命令,因为它是在history -a命令之后出现的。
说明 如果你打开了多个终端会话,仍然可以使用history -a命令在打开的会话中
向.bash_history文件中添加记录。但是对于其他打开的终端会话,历史记录并不会自动更
新。这是因为.bash_history文件只有在打开首个终端会话时才会被读取。要想强制重新读
取.bash_history文件,更新终端会话的历史记录,可以使用history -n命令。
你可以唤回历史列表中任意一条命令。只需输入惊叹号和命令在历史列表中的编号即可。
$ history
[…]
13 pwd
14 ls
15 cd
16 type pwd
17 which pwd
18 type echo
19 which echo
20 type -a pwd
21 type -a echo
[…]
32 history -a
33 history
34 cat .bash_history
35 history
$
$ !20
type -a pwd
pwd is a shell builtin
pwd is /bin/pwd
$
编号为20的命令从命令历史记录中被取出。和执行最近的命令一样,bash shell首先显示出从
shell历史记录中唤回的命令,然后执行该命令。
使用bash shell命令历史记录能够大大地节省时间。利用内建的history命令能够做到的事情
远不止这里所描述的。可以通过输入man history来查看history命令的bash手册页面。
2. 命令别名
alias命令是另一个shell的内建命令。命令别名允许你为常用的命令(及其参数)创建另一
个名称,从而将输入量减少到最低。
你所使用的Linux发行版很有可能已经为你设置好了一些常用命令的别名。要查看当前可用
的别名,使用alias命令以及选项-p。
$ alias -p
[…]
alias egrep=’egrep —color=auto’
alias fgrep=’fgrep —color=auto’
alias grep=’grep —color=auto’
alias l=’ls -CF’
alias la=’ls -A’ alias ll=’ls -alF’
alias ls=’ls —color=auto’
$
注意,在该Ubuntu Linux发行版中,有一个别名取代了标准命令ls。它自动加入了—color
选项,表明终端支持彩色模式的列表。
可以使用alias命令创建属于自己的别名。
$ alias li=’ls -li’
$
$ li
total 36
529581 drwxr-xr-x. 2 Christine Christine 4096 May 19 18:17 Desktop529585 drwxr-xr-x. 2 Christine Christine 4096 Apr 25 16:59 Documents
529582 drwxr-xr-x. 2 Christine Christine 4096 Apr 25 16:59 Downloads
529586 drwxr-xr-x. 2 Christine Christine 4096 Apr 25 16:59 Music
529587 drwxr-xr-x. 2 Christine Christine 4096 Apr 25 16:59 Pictures
529584 drwxr-xr-x. 2 Christine Christine 4096 Apr 25 16:59 Public
529583 drwxr-xr-x. 2 Christine Christine 4096 Apr 25 16:59 Templates
532891 -rwxrw-r—. 1 Christine Christine 36 May 30 07:21 test.sh
529588 drwxr-xr-x. 2 Christine Christine 4096 Apr 25 16:59 Videos
$
在定义好别名之后,你随时都可以在shell中使用它,就算在shell脚本中也没问题。要注意,
因为命令别名属于内部命令,一个别名仅在它所被定义的shell进程中才有效。
$ alias li=’ls -li’
$
$ bash
$
$ li
bash: li: command not found
$
$ exit
exit
$
不过好在有办法能够让别名在不同的子shell中都奏效。下一章中就会讲到具体的做法,另外
还会介绍环境变量。