通过利用 Bourne-again shell (bash) 的重要组成部分及其功能来简化系统管理任务。Bash 是一个流行的 Shell,是对原始 Bourne 和 Korn Shell 的替代。它提供了印象深刻的系列附加功能,其中包括对脚本环境的改进、广泛的别名技术和用于自动完成各种命令、文件和路径的改进方法。
关于本系列
典型的 UNIX 管理员拥有一套经常用于辅助管理过程的关键实用工具、诀窍和系统。存在各种用于简化不同过程的关键实用工具、命令行链和脚本。其中一些工具来自于操作系统,而大部分的诀窍则来源于长期的经验积累和减轻系统管理员工作压力的要求。本系列文章主要专注于最大限度地利用各种 UNIX 环境中可用的工具,包括简化异构环境中的管理任务的方法。
Bash 背景介绍
UNIX 和 Linux 环境下的 Shell 通常可归入两个类别之一,这两个类别基于最初的 UNIX 版本所附带的原始 Shell。它们分别是 Bourne Shell 和 C Shell,后者的独特之处在于其格式和结构类似于 C 编程语言的格式和结构。
Bourne Shell 比 C Shell 更容易使用和理解,但是对于您可能希望在 Shell 编程环境中完成的更复杂脚本编程,它可能就不太实用了。Korn Shell 提供了 Bourne Shell 的易用性和附加的作业控制扩展(允许您容易地管理多个后台作业)、命令行编辑和历史记录,以及用于简化编程的附加 C Shell 元素。
Bourne-again shell (bash) 是一个开放源代码项目,它组合了 Bourne Shell 的原理、C Shell 的编程环境、Korn Shell 的扩展功能,以及它自己的许多扩展,这些扩展不仅为 Shell 脚本编程提供了丰富的环境,而且还用作交互式的 shell 环境,用于控制并与计算机交互。
命令行编辑和键绑定
bash 中的主命令提示符同时提供了编辑命令行的能力和历史记录功能,后者记住各个命令行,以便您能够再次执行它们。
编辑功能意味着您可以在当前显示的命令行中向前和向后移动,以做出更改或纠正输入错误。在标准 bash 形式中,您可以使用光标键来执行基本的移动。更广泛的命令(例如按单词向前和向后移动)可以通过 Readline 库来控制,该库在缺省情况下同时支持 vi 和 emacs 绑定。若要设置编辑模式,可以在命令行上或在引导文件中指定您的首选模式:$ set editing-mode emacs。
例如,使用 emacs 编辑模式,以下键绑定将生效:
* Control-A——此键绑定将当前位置定位到行开头。
* Control-E——此键绑定将当前位置定位到行尾。
* Control-K——此键绑定删除截止行尾的所有内容。
* Meta-B——此键绑定后退一个单词。
* Meta-F——此键绑定向前推进一个单词。
* Meta-D——此键绑定删除当前单词。
使用内部 bind bash 命令,您实际上可以随心所欲地将任何键或组合绑定到某个特定的操作。首先,您可以通过使用 -P 选项(请参见清单 1)来获取可用命令列表。
清单 1. 使用 -P 选项来获取可用命令列表
-
$ bind -P
-
-
abort can be found on "\C-g", "\C-x\C-g", "\M-\C-g".
-
accept-line can be found on "\C-j", "\C-m".
-
alias-expand-line is not bound to any keys
-
arrow-key-prefix is not bound to any keys
-
backward-byte is not bound to any keys
-
...
-
yank can be found on "\C-y".
-
yank-last-arg can be found on "\M-.", "\M-_".
-
yank-nth-arg can be found on "\M-\C-y".
-
yank-pop can be found on "\M-y".
复制代码
\C 引用 Control 键。\M 序列引用“Meta”键(某些键盘上的专用键,或者通常为 Alt 键或 Escape 键)。
若要设置绑定,您必须指定键序列和要执行的命令,它们之间用分号分隔,并且键序列要用双引号来转义(在极端情况下,您可能需要使用单引号来对该形式进行再次转义)。例如,若要将 Control-B 更改为逐个单词地向后移动,可以使用 $ bind "\C-b":backward-word。
您甚至可以使用绑定来执行 Shell 命令(例如,运行某个应用程序)。为此,您必须添加 -x 选项,这是需要两次转义的一个示例。例如,若要将 Control-E 设置为运行 emacs,您要使用以下命令:$ bind -x '"\C-e"':emacs。
若要让 bash 中的键绑定在每次都启用,您可以设置 .inputrc 文件中的信息(然后此文件将影响所有启用了 Readline 的应用程序),或者您可以在启动脚本中放置特定的 bash 绑定,本文稍后将对此进行介绍。
命令历史记录
您输入的每个命令都被记录下来,以便您能够返回到该命令,并再次按原样运行它,或者编辑它并运行编辑后的版本。使用 Control-P(上一个命令)和 Control-N(下一个命令),您可以在命令历史记录中向后或向前移动(按反向的年代顺序)。如果已经遍历完前面的所有命令,则只能移动到下一个命令。
如果您知道命令的内容,但是想不起它的位置,您可以使用 Control-R 来执行反向智能搜索。这将返回到与您键入的第一个字母相匹配的第一个命令(以反向顺序);随后键入的字母将使匹配变得越来越明确。一旦找到您想要的命令,按 Return 即可按原样运行它。若要编辑您找到的命令,可以使用光标键(或键绑定)来移动到您希望编辑的位置。
自定义提示符
所有 Shell 都允许您自定义命令行上显示的提示符,这通常是通过设置 PS1 变量来实现的。这通常仅限于能够设置某个静态字符串,或者对于 Korn Shell,您通常可以设置某个动态值,例如当前目录。
Bash 进一步扩展了此功能,允许您包含用户名、主机名甚至隐藏字符(例如,设置颜色或设置窗口和 Xterm 标题所需的转义)。所指定的字符串将紧跟在显示提示符之前求值,因此它始终是最新的。
特定动态值的自定义扩展是使用一系列反斜杠字符来实现的。例如,若要设置包含用户名和当前目录最后一部分的典型提示符,可以使用以下命令:PS1="\u \W$"。
更典型的解决方案是显示用户名、主机名、目录,以及美元符号或井号(具体取决于当前用户的 UID)。后一个选项在有效用户 ID 为零 (root) 时显示一个井号:PS1='\u@\h:\W \$ "。
表 1 显示了可用扩展的完整列表。
表 1. 可用扩展列表
转义字符
|
描述
|
\t
|
\t 是时间,格式为 HH:MM:SS。
|
\d
|
\d 是日期,格式为“工作日名称 月份 日期”(例如,星期五 10 月 13 日)。
|
\n
|
\n 是换行。
|
\s
|
\s 是 Shell 名称(通常为 bash,如果该 Shell 是登录 Shell,则为 '-bash')。
|
\w
|
\w 是当前目录的完整路径。
|
\W
|
\W 是当前目录的最后一部分(换句话说,就是 '/home/mc' 中的 'mc')。
|
\u
|
\u 是您的用户名。
|
\h
|
\h 是主机名。
|
\#
|
\# 是此命令的命令编号(该值在每行递增一)。
|
\!
|
\! 是此命令的唯一历史记录编号。
|
\nnn
|
\nnn 是由八进制 nnn 所标识的字符。
|
\$
|
\$ 在有效 UID 为零时为 # 字符,否则为 $ 字符。
|
\\
|
\\ 是反斜杠。
|
\[
|
\[ 开始一个转义序列;一般用于颜色设置和标题的终端控制字符。
|
\]
|
\] 结束转义序列。
|
转义序列可用于发送终端控制序列,后者又可用于设置颜色和终端标题。由于 bash 会在每次显示提示符时更新此信息,这会自动更新相应的窗口标题。例如,可以使用清单 2 中的以下序列来将终端窗口和 Xterm 标题设置为 user@hostname:fullpath,并将提示符设置为 hostname:endpath 加上适当的终端字符(美元符号或井号)。
清单 2. 设置终端标题和提示符
-
PS1=" \[\033]0;\u@\h: \w\007\]\[\033[31m\]\h \[\033[34m\]\W \$ \[\033[00m\]"
复制代码
此外,主机名为 root 时是红色,为普通用户时为绿色,提示符中的其余部分则设置为青色。
文件和目录自动完成
为帮助提高您的 Shell 交互速度,bash 包含路径名自动完成功能——也就是说,您可以让 bash 填充路径名的其余部分,或者为您提供可能的展开列表。
例如,如果您查看一下当前目录,可以看到一系列不同的子目录(请参见清单 3)。
清单 3. 当前目录中的子目录
-
$ ls -f
-
back/ cheffyhack/ edin/ logstomerge/ mysql-binlogs/ svn/
-
build/ cvs/ install/ lost+found/ mysqlsizer vmware/
-
calc/ dbdumps/ logs/ my.cnf statmon/ webs/
复制代码
若要切换到 edin 目录,您可以输入:$ cd edin。或者,使用 bash 中的自动完成功能,您可以输入 $ cd e 然后按 TAB 键。缺省情况下,这会尝试展开以 'e' 开头的路径名;此目录中的结果应该是 $ cd edin。
如果存在多个匹配项,则第一次按 TAB 将会失败;第二次按 TAB 将返回匹配的路径列表(请参见清单 4)。
清单 4. 返回匹配的路径列表
-
$ cd my
-
my.cnf mysql-binlogs/ mysqlsiz
复制代码
er
展开过程将继续执行,并尽可能将每个部分替换为唯一组件,或者直到无法进一步展开或只存在唯一的路径。例如,请考虑清单 5 中显示的以下序列。
清单 5. 继续展开
-
$ cd my
-
my.cnf mysql-binlogs/ mysqlsizer
-
$ cd mys
-
$ cd mysql
-
mysql-binlogs/ mysqlsizer
-
$ cd mysqls
-
$ cd mysqlsizer
复制代码
如果路径名是一个目录,您可以继续展开该路径名的每个部分。
别名
别名是一种简单的机制,允许您为给定的序列提供展开形式。由于别名是一种展开形式而不是替换,它使您可以继续向别名命令添加选项和参数。
例如,设置别名 ('ll') 是很普遍的,该别名展开为 'ls -l',用于获得目录或文件列表的长清单。可以使用以下命令行来创建别名 alias ll='ls -l'。
若要使用该命令,您只需在提示符下输入别名 $ ll。
由于别名不过就是实际命令的别名,因此您可以添加进一步的选项,例如,为了列出目录中的所有文件,您可以使用 $ ll -a,它实际上展开为 $ ls -l -a。
别名是快速运行命令或使用您经常使用的命令行选项来运行您最喜欢的命令的有效方法。
使用目录堆栈
目录堆栈使您可以将一个或多个目录存储到某个临时区域中,然后再次调回它们。该目录列表的行为就像一个堆栈——您将一个目录压入堆栈顶部,并从顶部弹出一个目录以取回它。
若要将某个目录压入堆栈,您可以使用 pushd 和您希望放在堆栈上的目录——例如,您可以压入当前目录,该目录可由单个句点来标识(请参见清单 6)。
清单 6. 压入当前目录
-
$ pushd .
-
/export/data /export/data
复制代码
Bash 使用您压入的目录和当前在堆栈中的所有目录列表(目录之间用一个空格来分隔)来作出响应。通过切换目录并再次压入目录,您可以更清楚地看到这点(请参见清单 7)。
清单 7. 压入另一个目录
-
$ cd /etc
-
$ pushd .
-
/etc /etc /export/data
复制代码
使用 popd 将取走添加到堆栈上的最后一个目录,并切换到该目录(请参见清单 8)。
清单 8. 使用 popd
-
$ cd /usr/local/bin
-
$ popd
-
/etc /export/data
-
$ pwd
复制代码
popd 命令在切换到新目录前返回目录列表。
当您希望临时切换目录然后返回原始目录时,通常使用此目录功能。为了简化此类情况,通常为 bash 配置了两个别名,即 pu 和 po,如清单 9 所示。
清单 9. Pu 和 po 别名
-
$ alias pu='pushd .'
-
$ alias po='popd'
复制代码
例如,假设您正在某个目录中工作,突然认识到需要在另一个目录中创建一个 tar 存档(请参见清单 10)。
清单 10. 使用 pu 和 po 来创建 tar 存档
-
$ pwd
-
/usr/local/bin
-
$ pu
-
/usr/local/bin /usr/local/bin
-
$ cd /export/data
-
$ tar cf webs.tar ./webs
-
$ po
-
$ pwd
-
/usr/local/bin
复制代码
尽管目录堆栈系统支持多个目录,但通常是在您临时弹出到其他目录时将它用于一个或两个目录。
Bash 运行时配置文件
在登录时,bash 支持标准的 .profile 文件(Bourne Shell 所使用的配置文件),除此之外还支持它自己的特定 .bash_profile。要使用的文件按如下方式进行选择:
* 如果 ~/.bash_profile 存在,则使用它,否则:
* 如果 ~/.bash_login 存在,则使用它,否则:
* 如果 ~/.profile 存在,则使用它。
对于不是作为登录 Shell 来启动的 bash Shell(例如,在使用 Xterm 或启动某个子 Shell 的其他应用程序时),bash 将寻找 ~/.bashrc 文件(如果它存在的话)。
由于 .bashrc 和 .bash_profile 的内容不大可能会更改,查找包含清单 11 所示内容的 .bash_profile 的情况并不鲜见。
清单 11. .bash_profile 内容
-
if [ -f ~/.bashrc ];
-
then
-
source ~/.bashrc
-
fi
复制代码
除了这些规则外,那些文件的内容完全取决于您。由于 bash 环境中存在如此多的自定义,您还可能经常发现 .bashrc/.bash_profile 内容不过是源于许多其他 ~/.bash_* 脚本内容的包装。例如,您可能将配置划分为:
* ~/.bash_aliases——用于存储所有自定义别名和函数
* ~/.bash_path——用于存储所有路径指定
* ~/.bash_vars——用于存储所有 bash 变量
然后又在 .bashrc 中引用它们的内容(请参见清单 12)。
清单 12. .bashrc 内容
-
if [ -f ~/.bash_path ]; then
-
source ~/.bash_path
-
fi
-
-
if [ -f ~/.bash_vars ]; then
-
source ~/.bash_vars
-
fi
-
-
if [ -f ~/.bash_aliases ]; then
-
source ~/.bash_aliases
-
fi
复制代码
您应该小心确保以正确的顺序引用那些文件。
脚本改进
bash 提供的某些较大改进的最后一个方面是脚本功能。对于大多数 Shell,人们经常提到的局限性之一在于,它们具有松散的变量类型、没有数据数组支持、没有用于执行基本的数学或表达式计算的内置功能。所有这些问题都在 bash 中得到不同程度的解决。
bash 中的变量可以在使用前声明,并且声明可以包括类型。例如,若要将某个变量声明为整数类型(从而始终将其标识为一个有效数字),可以使用:$ declare -i myint。若要同时设置值,可以使用 $ declare -i myint=235。
若要执行基本的算术运算,您可以在 $(( )) 中嵌入表达式(请参见清单 13)。
清单 13. 嵌入表达式以执行基本的算术运算
您还可以包括变量(请参见清单 14)。
清单 14. 包括变量
-
$ echo $((myint+3*12))
-
63
复制代码
若要将某个变量声明为数组类型,可以使用 $ declare -a myarray。
您可以通过指定带括号的值列表来添加值:$ declare -a myarray=(tom dick harry)。
若要获取数组中的值,您可以指定数组引用(请参见清单 15)。
清单 15. 指定数组引用
-
$ echo ${myarray[1]}
-
dick
复制代码
您可以使用同样的机制来填充数组,例如,使用文件列表来填充数组(请参见清单 16)。
清单 16. 使用文件列表来填充数组
-
$ declare -a files=`ls`
-
$ echo $files
-
back/ build/ calc/ cheffyhack/ cvs/ dbdumps/ edin/ install/ \
-
logs/ logstomerge/ lost+found/ my.
-
cnf mysql-binlogs/ mysqlsizer statmon/ svn/ vmware/ webs/
复制代码
而且,通过使用 @ 符号来指定元素,您可以使用数组的整个内容(请参见清单 17)。
清单 17. 使用 @ 符号来指定元素
-
for file in ${files[@]}
-
do
-
echo $file
-
done
复制代码
这种操作和变量支持使得 Shell 脚本的许多编程方面变得更加容易了。
总结
相对于传统的 Bourne、Korn 和 C Shell,Bash 提供了许多重要改进。这些扩展中的大多数都改善交互式用户(即使用 bash 作为其与系统交互的主要方法的用户)的体验。有些机制成了习惯,例如路径名自动完成和别名;而其他机制则对个别场合至关重要。目录堆栈机制就是一个很好的例子;当您希望快速访问某个目录而不打断您的思维时,它就变得非常有价值了。
无论使用 bash Shell 中的什么元素,您都会发现一个丰富的环境和几乎无限的自定义系列,您可以使用它们来改进您的交互和环境。
阅读(1257) | 评论(0) | 转发(0) |