5.4 其它妙招

在本节中,我们介绍其它几个必备的命令行使用妙招。

5.4.1 命令替换

假设我们想要编辑包含 error 的 Python 源代码文件 (*.py),在此之前,我们首先需要确定哪些 Python 文件具有这些字符串。为此,我们可以使用下面的命令将其找出来:

xiaodong@codeland:~$ grep -l error *.py

这里的 -l 选项让 grep 命令将找出的文件名输出到终端,然后我们就可以用文本编辑器 (如 nvim) 来编辑这些文件:

xiaodong@codeland:~$ nvim godns.py

代替这种需要两步才能完成的操作,我们也可以要求 Shell 在执行 grep 命令后直接将文件名传递给文本编辑器 (如 nvim):

xiaodong@codeland:~$ nvim `grep -l error *.py`

Shell 对上述命令 `` (反引号) 中的内容执行命令替换,也就是说,把 grep 命令的输出作为 nvim 命令的参数。我们从而将两步操作得以合并成一步操作来完成。

使用 `` (反引号) 算是老式的命令替换用法,更新式的用法是 $()。所以上面的命令也能改写成如下形式:

xiaodong@codeland:~$ nvim $(grep -l error *.py)

我们推荐使用 $() 这种新用法,因为在嵌套命令替换时具有更好的可读性。例如:

xiaodong@codeland:~$ nvim $(grep -l failed $(date +'%Y%m%d').log)
xiaodong@codeland:~$ nvim `grep -l failed \`date +'%Y%m%d'\`.log`

在嵌套时,$() 看起来一目了然,而 `` (反引号) 则需要转义,其可读性较差。

5.4.2 使用变量

虽然 Shell 本身具有内置变量,而且我们在执行程序时还会碰到环境变量,但是我们在此要讲的却是另一种变量,即用户变量。

用户变量是由我们自己所设置的变量,其目的在于临时保存需要多次使用的数据。这样,当我们需要使用数据时就可以通过变量名来引用它了。因为通常变量名比我们要引用的数据要短很多,所以也让我们减少了不必要的重复输入。

让我们看一个例子:

xiaodong@codeland:~$ LOG=/var/log/pacman.log
xiaodong@codeland:~$ head $LOG
xiaodong@codeland:~$ grep -i error $LOG

我们将变量 LOG 的值设置为 /var/log/pacman.log,接着通过 $LOG 的形式分别在 headgrep 命令中引用它。

5.4.3 重复执行命令

当我为演讲主题准备材料时,我想用 figlet 这个工具来制作一些有趣的 ASCII 艺术字。虽然 figlet 提供了很多艺术样式,但是我并没有见过每一种。要想选择最酷的 ASCII 艺术字,所以我必须把每种样式都浏览一遍。于是,我执行了下面的命令:

xiaodong@codeland:~$ figlet -f ascii9 Linux
xiaodong@codeland:~$ figlet -f bigmono9 Linux
xiaodong@codeland:~$ figlet -f emboss Linux
...

可是,figlet 还有很多样式,这样一个一个的查看实在枯燥乏味。Shell 有没有什么重复执行命令的快捷方法呢?回答是肯定的。利用 Shell 提供的 for 循环,我们可以一遍又一遍的重复执行命令。下面让我们来一次性查看 figlet 的所有样式。

xiaodong@codeland:~$ cd /usr/share/figlet
xiaodong@codeland:~$ for font in *.tlf
> do
>     echo "Font: $font"
>     figlet -f $(basename $font .tlf) Linux
> done

forin 中间的 font 为循环的变量,*.tlf 表示扩展名为 tlf 的所有文件。dodone 之间的内容为循环体,针对当前目录下的每个 tlf 文件都会执行这两条命令。

除了上面多行形式的 for ... in 循环结构外,我们也可以使用其单行形式,例如:

for font in *.tlf; do figlet -f $(basename $font .tlf) Linux; done