如果您管理多种UNIX系统(特别是在异构环境中),则最艰巨的任务可能是在不同环境之间切换并执行不同的任务,同时还必须考虑系统之间的所有差异。典型的UNIX 管理员拥有一套经常用于辅助管理过程的关键实用工具、诀窍和系统。存在各种用于简化不同过程的关键实用工具、命令行链和脚本。其中一些工具来自于操作系统,而大部分的诀窍则来源于长期的经验积累和减轻系统管理员工作压力的要求。
例如,ps 命令在基于BSD 和基于SVR4 的UNIX 主机上,分别需要不同的命令行选项来获得大致相同的信息。平台之间还存在更广泛的差异。有时,这种差异是命令名称发生了更改;Linux 提供adduser 命令,而Solaris 则提供useradd 命令。 您可以创建自己的一套执行特定任务的脚本(包括您自己用于 ls、ps 等常用工具的替代脚本),以便它们生成您想要的信息。这样做有点危险,原因是它意味着您可能从未使用原始命令,从而可能在您的脚本不可用时导致潜在的问题。
典型UNIX管理员使用命令可能的解决方案有以下三种:
◆别名:这种解决方案仅在某些外壳中受支持——别名提供了将给定的字符串展开为特定命令的简单方法。
◆外壳函数:大多数现代外壳都支持这种解决方案——外壳函数使您能够创建更复杂的序列,但是由于它们作为内置函数运行,在差异相当小时可能更为实用。
◆外壳脚本:当您要构建的包装特别复杂时,更好的解决方案是使用外壳脚本,您可以代替原始命令调用这些脚本。使用外壳脚本,您可以更创造性地处理替代,甚至为另一个命令提供完全由外壳脚本驱动的替代。
使用别名机制操作Unix命令
别名在 Korn (ksh)、Bourne-Again SHell (bash)、TENEX C shell (tcsh) 和 Z shell (zsh) 外壳中受支持,当您希望设置命令的特定选项,同时仍然支持其他选项时,别名提供了也许是最简单的方法。顾名思义,您可以将一个命令用作另一个命令的别名,或者为带有附加选项的同一个命令提供别名。别名从您键入的内容展开为其展开形式。
例如,一个常用的别名是ll,它调用等效的 ls -l(ll 通常称为长清单 (long listing))。每当用户键入ll,就会直接将其替换为展开形式,因此:$ ll a* 在执行前展开为:$ ls -l a*。 命令行选项也仍然有效,换句话说,$ ll -a 展开为:$ ls -l -a。 还可以为现有命令设置别名;假设将 -F 选项添加到所有 ls 命令,这样,$ ls 将展开为:$ ls -F。 要设置别名,请使用内置的外壳 alias 语句,并在引号中指定所需的展开形式。例如,要设置前面详细描述的 ll 的展开形式,可使用:$ alias ll='ls -l'。
比如ps 命令,它在基于SVR4 和基于BSD 的UNIX 主机上是不同的。 在 BSD 上指定别名 $ alias ps='ps -o pid,ppid,command' 而在SVR4 主机上指定别名 $ alias ps='ps -opid,ppid,cmd 现在,在这两个系统对 ps 的不同操作方式的限制下,您获得了ps 产生的标准输出。和前面一样,您可以继续添加更多选项;例如,在安装了该别名的任一个平台上请求所有进程,添加 -A 选项就是这样一种情况。
同样以ps为例,您可以创建别名ps-all 来输出所有进程列表,并根据需要为每种平台设置相应的展开形式。
设置这些别名的最佳位置是在登录期间执行的外壳初始化脚本中,例如 .ksh、.profile 或 .bashrc。您可以在这些脚本中执行同样的系统检查,以验证要启用哪些别名。如果希望提供适用于所有用户的全局解决方案,则应将别名定义放在公开可用的文件中(例如放在 /etc or /usr/local 中),并设置用户初始化脚本以获得别名定义来源。
别名机制最适合于您希望设置单个命令的命令行选项的情况,虽然也可以使用它们来将给定的命令展开为一组命令或管道。这样削弱了为展开形式中除最后一个命令以外的其他任何命令指定附加参数的能力。对于处理此类包装,外壳中的内联函数可能更为适合。 大多数外壳都支持函数,这些函数本质上是微型脚本,您可以在其中放置命令和其他外壳脚本元素以执行特定的任务。它们是主外壳定义中的函数,支持许多完整外壳脚本所具有的相同功能,如命令行参数。对于支持别名无法在其中工作的某些命令和组合,对命令行参数的支持非常关键。
例如,killall 命令最基本的功能是终止所有与特定字符串匹配的命令。在Solaris 上,killall 命令存在,但是将其用作关闭过程的一部分以终止所有进程。设想在 Solaris 主机上意外调用 killall 命令以关闭所有 Apache 进程,没想到却实际上关闭了系统!
如果要在所有主机上使用相同的名称或使用不同的名称实现按名称终止进程的预期结果,并消除不希望的和可能代价高昂的错误,同时扩展本身并不支持该选项的系统的功能。可使用kill 命令将 KILL 信号发送到每个匹配进程。在命令行上,您可以通过一系列管道实现等效的功能(使用 KILL 信号)。
提供killall 命令的替代 $ ps -ef|grep gcc|awk '{ print $2; }'|xargs kill -9 该命令的关键部分是提供给 grep的字符串和ps 输出中包含所需进程 ID 的列。上面的例子对 Solaris 主机和大多数 SVR4 UNIX 变种有效。
使用外壳函数的方法操作Unix命令
定义函数 function NAME() { # do stuff here } 调用函数时,函数参数作为 $1、$2 等形式来提供,就像在典型的外壳脚本中一样。因此,您可以定义一个函数,使其执行与 killall 相同的基于字符串的信号发送功能。例如定义一个执行与 killall 相同的信号发送功能的函数 function killall() { ps -ef|grep $1|awk '{ print $2; }'|xargs kill -9 } 请注意,该函数的 awk 部分中的 $2 不会展开,因为您已经对 awk 脚本定义使用了单引号,这样阻止了展开,并且在此示例中会挑选第二列。
函数的局限性在于,它们依赖外壳提供支持能力,而这并不总是可能或可用。虽然可以随心所欲地使内联外壳函数变得任意长,但在许多情况下,外壳函数并不理想。例如,在模拟更复杂的命令或提供命令包装的超长序列中,您需要分析选项并提供本地化的等效命令,此时内联函数就没有多大用处了。在这种情况下,外壳脚本可能更为适合。
使用外壳脚本的方式操作Unix命令
构建一致环境的最容易和最兼容的方法,是创建可用作实际命令的包装的外壳脚本,这样考虑了您希望支持的各种选项和设置。例如,useradd 和 adduser 命令在设置参数(如用户 ID 或组成员资格)时支持同样的单字母命令行选项,因此 Linux 上的 $ adduser -u 1000 -G sales,marketing mcbrown 等效于 Solaris 上的 $ useradd -u 1000 -G sales,marketing mcbrown。 然而,Linux 版本还支持扩展命令选项,例如,--uid 和 --groups 等效于上面的命令行选项。这些扩展选项在 Solaris 上不受支持,但是,如果创建一个名为 adduser 的外壳脚本,您就可以模拟 Linux 版本,然后用适当的选项运行实际的 Solaris useradd 命令。
例如,用作 adduser 或 useradd 命令的包装的示例外壳脚本。
|
以上的脚本的关键是 foreach 循环,它遍历所提供的命令行参数(在 $* 中提供)。对于每个选项,case 语句会尝试识别该选项——使用短格式或长格式并设置一个变量。命令行开关为 $1。如果该选项后面正常地跟着一个值(例如,用户 ID),您可以将 $2 当作该值来进行访问,并使用它将该值赋于某个变量。识别出某个选项后,shift 语句从 $* 变量列表中移动一个位置(若指定了数字,则移动指定数目的位置),以便已经识别出的命令行参数在循环的下一次迭代中不再在 $* 变量中。 识别并提取出可能的参数以后,您所需做的就是构建新的选项来提供给最终要使用的命令。由于useradd/adduser 都支持短格式的参数,所以可在此基础上构建新的命令选项字符串。这是通过检查对应的变量是否已设置并将该选项添加到命令行来实现的。请注意双引号的使用,它确保了原始命令中引用的参数被保留并得到正确识别。
通过使用命令行工具和外壳流控制(如 if 或case)的组合,您可以使用单个源来选择各种要使用的选项。有两个工具在这种情况下很有用:一个工具识别主机(如 hostname 或 uname),另一个工具识别平台 (uname)。 uname 产生的缺省输出是基本操作系统名称,如 Linux 或Solaris。例如,可以按照前一部分中的ps 示例,将该命令与case 语句结合使用以选择正确的别名,uname 的输出,例如
UNAME='uname' |
文章小结
规范化Unix命令使用环境对于简化管理大有帮助。它减轻了您的负担,帮助您确定所在的系统类型,以及哪个命令和/或选项集最适合于获取所需信息或执行所需操作。为每个命令选择正确的机制完全取决于该命令和您要尝试达到的目的。在您希望调用命令行选项的单个命令上,最好使用别名机制。内联函数最适合于您希望容易地将其嵌入当前脚本环境的更复杂操作和序列,而完整的单独脚本则最适合于麻烦的多步骤操作,或您希望为命令(或选项)提供支持而不更改外壳环境的场合。尽管有这些明显的优点,但是务必要记住,如果将自己过于屏蔽在基础的系统之外,当发生故障而您无法访问脚本时,您可能处于无准备的状态—您应该寻求扩展和扩充,而不是替代。