对话 UNIX,第 6 部分: 通过脚本实现操作的自动化( 二 )


end
命令行上的每个空格分隔的字符串变成了位置参数 , 包括所调用的脚本的名称 。因此 , 命令 synch.zsh 只有一个位置参数 $0 。synch.zsh --help 命令有两个位置参数:$0 和 $1 , 其中 $1 是字符串 --help 。
所以 , 清单 3 表示“如果第一个位置参数为空(-z 操作符测试空字符串)或(由 || 表示)如果第一个参数等于‘—help’ , 则打印用法信息 。(如果您刚开始编写脚本 , 可以考虑在每个脚本中提供用法信息作为提示 。它提醒其他人——甚至您自己 , 如果您忘了的话——如何使用该脚本 。)
短语 [[ -z $1 || $1 == "--help" ]] 是 if 语句的 条件 , 但您也可以将同样的条件子句用作命令 , 并将其与其他命令组合使用以控制通过脚本的流 。请查看清单 4 。它枚举您的 $PATH 中的所有可执行命令 , 并将条件与其他命令组合使用以执行适当的工作 。
清单 4. 列出 $PATH 中的命令
#! /bin/zsh
DirectorIEs=(`echo $PATH | column -s ':' -t`)
for directory in $directories
do
 [[ -d $directory ]] || continue
 
 pushd "$directory"
 
 for file in *
 do
 [[ -x $file && ! -d $file ]] || continue
 echo $file
 done
 
 popd
done | sort | uniq
此脚本中执行了相当多的操作 , 我们将它细分为以下几部分:
第一个实际脚本行——DirectorIEs=(`echo $PATH | column -s ':' -t`)——创建指定目录的数组 。您在 zsh 中通过将参数放在括号中来创建数据 , 例如 directories=(...) 。在此例中 , 数组元素是通过在每个冒号(column -s ':')处分拆 $PATH 以产生空格分隔的目录列表(column 的 -t 参数)来生成的 。
对于列表中的每个目录 , 该脚本尝试枚举该目录中的可执行文件 。步骤 3 至步骤 6 描述了该过程 。
[[ -d $directory ]] || continue 行是所谓的 short-circuiting 命令的一个示例 。short-circuiting 命令在其逻辑条件产生确定的结果时立即终止 。例如 , [[ -d $directory ]] || continue 短语使用逻辑“或(||)——它首先执行第一个命令 , 并且——当且仅当——第一个命令失败时才执行第二个命令 。因此 , 如果 $directory 中的条目存在 , 并且是一个目录(-d 操作符) , 则测试成功 , 求值结束 , 并且 continue 命令(它跳过当前元素的处理)永远不会执行 。
然而 , 如果第一个测试失败 , 则会执行该逻辑的下一个条件或执行 continue 。(continue 始终成功 , 因此它通常出现在 short-circuiting 命令的最后) 。
基于逻辑“与(&&) 的 Short-circuiting 首先执行第一个命令 , 并且——当且仅当——第一个命令成功时才执行第二个命令 。
pushd 和对应的 popd 分别用于在处理前切换到新目录和在处理后切换到先前的目录 。使用目录堆栈是一种理想的脚本技术 , 用于维持您在文件系统中的位置 。
内部的 for 循环枚举当前工作目录中的所有文件——通配符 *(星号)匹配所有条目——然后测试每个条目是否为文件 。[[ -x $file && ! -d $file ]] || continue 行表示“如果 $file 存在并且是可执行文件而且不是目录 , 则处理它;否则执行 continue 。
最后 , 如果前面的所有条件都满足 , 则使用 echo 来显示文件名 。
您弄明白该脚本的最后一行了吗?您可以将大多数控制结构的输出发送给另一个 Unix 命令——毕竟 , Shell 将该控制结构视为一个命令 。因此 , 整个脚本的输出通过 sort、然后通过 uniq 进行管道传输 , 以产生在您的 $PATH 中找到的唯一命令的字母排序列表 。
如果将清单 4 保存到一个名为 listcmds.zsh 的可执行文件 , 则输出可能类似如下:

推荐阅读