所有的 UNIX® 用户都应该了解如何使用 Korn Shell 脚本。通过编写 Shell 脚本,可以让您实现许多任务的自动化,并可以为您节约大量的时间。初看起来,它似乎令人生畏,但只要遵循正确的指导,您就可以熟练地使用它。本文将指导您编写自己的 Korn Shell 脚本。
什么是 Shell?
IBM® AIX® 操作系统和其他的类 UNIX 操作系统一样,都需要通过某种方式与内核进行通信。这项任务正是通过使用 Shell 来实现的。您可以使用各种不同的 Shell,但本文重点关注于 Korn Shell。Korn Shell 是 AIX 所使用的缺省 Shell。
当您登录到 AIX 中时,将以某个目录的提示符作为开始。缺省目录通常是您的 home 目录。之所以将其称为 home 目录,是因为该目录的结构通常如下所示:
$/home/jthomas: |
当登录时,您将处于命令行或者命令提示符处。这正是您输入 UNIX 命令的地方。您可以输入与 UNIX 内核进行交互的 Shell 命令。这些命令可能简单到只有一行(比如查看日期),也可能为多行,而这取决于您所进行的操作。清单 1 提供了一些示例命令。
清单 1. 示例命令
$date Fri May 1 22:59:28 EDT 2008 $uptime 10:59PM up 259 days, 9:44, 5 users, load average: 3.81, 14.27, 13.71 $hostname gonzo |
有关 Shell 命令的最棒的一项功能是,您可以将多个命令组合在一个称为脚本的文件中,它允许您依次运行多个命令。当您必须一次又一次重复地运行相同的命令时,使用脚本非常合适。您可以将这些命令放到一个 Korn Shell 脚本中,而无需反复地键入这些命令。
编写您的第一个 Korn Shell 脚本
Korn Shell 脚本中的第一行是 Shell 自身。它被表示为下面的形式:
#!/bin/ksh |
要在 AIX 中编写 Korn Shell 脚本,您需要使用一种文本编辑器。vi 是一种使用最广泛、且随处可见的文本编辑器。开始接触时可能会觉得有点麻烦,但随着使用 vi 的次数的增多,您将熟练地掌握它。关于如何使用 vi 文本编辑器,人们撰写了很多相关的书籍。
要开始编写您的第一个 Korn Shell 脚本,首先需要打开 vi 编辑器,并添加 Shell 名称作为第一行。完成这项操作之后,您需要构建某种类型的脚本标头,用来告诉编写脚本的用户,该脚本将执行什么操作,以及该脚本的编写时间。您可以对脚本进行任意地命名,但通常使用扩展名 .ksh 来表示 Korn Shell 脚本。您并不是必须要这样做,但这是一种很好的做法。镑符号 (#) 可用于对脚本进行注释,如清单 2 中所示。
清单 2. 脚本标头的示例
$vi my_first_script.ksh #!/bin/ksh ################################################### # Written By: Jason Thomas # Purpose: This script was written to show users how to develop their first script # May 1, 2008 ################################################### |
这个脚本标头非常简单,但它应用了上述的技巧。
变量
在脚本中设置变量是相当简单的。我通常使用大写形式来表示脚本内的所有变量,如清单 3 所示,但您不需要这样做。
清单 3. 变量的示例
#Define Variables HOME="/home/jthomas" #Simple home directory DATE=$(date) # Set DATE equal to the output of running the shell command date HOSTNAME=$(hostname) # Set HOSTNAME equal to the output of the hostname command PASSWORD_FILE="/etc/passwd" # Set AIX password file path |
Korn Shell 的具体细节
到目前为止,作为编写 Korn Shell 脚本的入门内容,您已经了解了如何编写基本的脚本标头,以及定义变量。现在,您可以开始编写一些 Korn Shell 代码了。
让我们开始从某个文件中读取一些行。在这个示例中,使用您已经在脚本中定义过的 /etc/passwd 文件,并且仅打印用户名称,如清单 4 中所示。
清单 4. for 循环
$vi my_first_script.ksh #!/bin/ksh ################################################### # Written By: Jason Thomas # Purpose: This script was written to show users how to develop their first script # May 1, 2008 ################################################### #Define Variables HOME="/home/jthomas" #Simple home directory DATE=$(date) # Set DATE equal to the output of running the shell command date HOSTNAME=$(hostname) # Set HOSTNAME equal to the output of the hostname command PASSWORD_FILE="/etc/passwd" # Set AIX password file path #Begin Code for username in $(cat $PASSWORD_FILE | cut -f1 -d:) do print $username done |
这种语法形式被称为 for 循环。它允许您打开 /etc/passwd 文件,并且每次读取其中的一行内容,仅截取文件中的第一个字段,然后打印这行内容。请注意这个特殊字符:|。我们称其为管道。管道允许您将一个命令的输出重定向到另一个命令。
在保存该脚本之后,您可以运行它,如清单 5 中所示。
清单 5. 运行脚本
$./my_first_script.ksh root daemon bin sys adm uucp nobody lpd |
该脚本开始将输出打印到屏幕上。或者,您可以通过进行下面的操作仅将输出打印到一个文件中:
print $username >> /tmp/usernames |
>> 告诉打印命令仅将每个用户名依次追加到一个文件中。通过进行这项操作,在您的终端中将不会再显示相应的文本。您还可以通过使用下面的命令,将输出同时打印到屏幕和文件中:
print $username | tee –a /tmp/usernames |
tee 命令允许您同时将输出重定向到终端和文件。
您刚刚了解了如何使用 for 循环读取文件的内容,以及如何仅截取用户名、并将输出重定向到文件或者终端。
错误检查
如果开始的时候 /etc/passwd 文件并不存在,那么会发生什么情况呢?简单来说,该脚本将会失败。清单 6 显示了用来检查该文件是否存在的语法。
清单 6. 用来检查某文件是否存在的语法
#Begin Code if [[ -e $PASSWORD_FILE ]]; then #Check to see if the file exists and if so then continue for username in $(cat $PASSWORD_FILE | cut -f1 -d:) do print $username done else print "$PASSWORD_FILE was not found" exit fi |
这一小段代码显示了条件型 if 语句。如果 /etc/passwd 文件存在,那么该脚本将继续执行。如果这个文件不存在,那么该脚本将在终端屏幕上打印 "/etc/passwd file was not found",然后退出。条件型 if 语句以 if 开头,以反写的字母 (fi) 结尾。
美元问号符 ($?)
每当您在 AIX 中运行一个命令时,系统将设置一个变量,它通常被称为美元问号符。AIX 将这个变量设置为零,以表示成功;设置为非零,以表示失败。这对于 Korn Shell 脚本来说是非常有用的。清单 7 介绍了当您运行有效和无效的 AIX 命令时查看 $? 变量的设置。
清单 7. 针对有效和无效 AIX 命令,如何设置 $?
$date Sat May 10 00:02:31 EDT 2008 $echo $? 0 $uptime 12:02AM up 259 days, 10:47, 5 users, load average: 4.71, 10.44, 12.62 $echo $? 0 $IBM ksh: IBM: not found. $echo $? 127 $aix ksh: aix: not found. $echo $? 127 $ls -l /etc/password ls: 0653-341 The file /etc/password does not exist. $echo $? 2 |
在编写 Korn Shell 脚本时,这是非常有价值的,因为它向您提供了另一种检查错误的方式。下面是用于检查 /etc/passwd 文件是否存在的另一种不同的方式:
#Begin Code PASSWORD_FILE="/etc/passwd" ls –l $PASSWORD_FILE > /dev/null 2>&1 |
这个命令允许您列出该文件。然而,您并不是真的在意这个文件是否存在。对于您来说,重要的是获得该命令的返回代码。大于符号 > 允许您对该命令的输出进行重定向。在本文稍后的内容中,您将了解更多有关重定向输出的信息。
清单 8 显示了如何在脚本中使用 $?。
清单 8. 在脚本中使用 $?
#Begin Code PASSWORD_FILE="/etc/passwd" ls –l $PASSWORD_FILE > /dev/null 2>&1 if [[ $? != 0 ]]; then print “$PASSWORD_FILE was not found" exit else for username in $(cat $PASSWORD_FILE | cut -f1 -d:) do print $username done fi |
我尝试了列出该文件,而不是实际地进行检查,以判断该文件是否存在。如果您可以列出该文件,则表示该文件存在。如果您无法列出该文件,则表示该文件不存在。在 AIX 中可以通过使用 ls ¨Cl filename 命令列出文件。这项操作使得您可以通过检查 $? 变量来测试 AIX 命令是否成功执行。
标准输入、输出和错误
您真的需要了解这些内容。一般说来,主要存在三个输入和输出源。在 AIX 中,它们分别称为 STDIN、STDOUT 和 STDERR。STDIN 指的是您可能从键盘获得的输入。STDOUT 是执行一个命令时打印到屏幕上的输出。STDERR 则对应于一个命令失败时的屏幕输出。STDIN、STDOUT 和 STDERR 的文件描述符分别映射到数值 0、1 和 2。
如果希望检查一个命令成功或者失败,那么您可以进行类似清单 9 中的操作。
清单 9. 将输出重定向到 STDOUT 和 STDERR
$date > /dev/null 2&&1 # Any output from this command should never be seen if [[ $? = 0 ]]; then print "The date command was successful" else print "The date command failed fi |
这段代码运行了 AIX 中的 date 命令。您应该不会看到任何来自 STDOUT(文件描述符 1)或者 STDERR(文件描述符 2)的输出。然后,您使用条件型 if 语句,以检查该命令的返回代码。如前所述,如果该命令返回零,那么该命令的执行是成功的;如果它返回的是非零,那么它的执行是失败的。
函数
在 Korn Shell 脚本中,单词 function 是一个保留字。可以使用函数将脚本划分为多个部分。在您调用函数时,仅运行相应的片断。在已编写的代码的基础上,创建一个错误检查函数,如清单 10 所示。
清单 10. 错误检查函数
################## function if_error ################## { if [[ $? -ne 0 ]]; then # check return code passed to function print "$1" # if rc > 0 then print error msg and quit exit $? fi } |
如果我希望在脚本中运行一个简单命令,那么我可以简单地编写一些类似于上面 $? 的错误检查代码。每当我希望检查某些操作是否失败时,我还可以仅调用 if_error 函数,如清单 11 所示。
清单 11. 调用 if_error 函数
rm –rf /tmp/file #Delete file if_error "Error: Failed removing file /tmp/file" mkdir /tmp/test #Create the directory test if_error "Error: Failed trying to create directory /tmp/test" |
每当运行上面的任何一个命令时,就会调用一次 if_error 函数。对应于该特定错误检查的消息将传递给 if_error 函数。这一点很重要,因为它允许您编写一次 Shell 脚本代码,却可以一次又一次地使用它。这样可以更快、更简单地编写 Shell 脚本。
case 语句
case 语句是另一种条件型语句,您可以用该语句来替代 if 语句。case 语句以 case 开头,并且以反写的 (esac) 结尾。当您的脚本变得比较复杂、并且需要执行不同的任务时,可以使用 case 语句迅速地进行构建。清单 12 提供了一个示例。
清单 12. case 语句
case value in "Mypattern") commands to execute when value matches Mypattern ;; esac |
假设您希望在某天中不同的时刻删除一个文件。那么您可以创建一个变量,用于检查具体的时刻:
TIME=$(date +%H%M) |
清单 13 中所显示的代码将在晚上 10:00 和晚上 11:00 删除某个文件。因此,每次执行这个代码段的时候,将检查 $TIME 是否匹配 case 语句所指定的时间。如果匹配,那么将执行相应的代码。
清单 13. 用于检查时间的 case 语句
case $TIME in "2200") #This means 10:00 rm –rf /tmp/file1 ;; "2300")#This means 11:00 rm –rf /tmp/file1 ;; "*") echo "Do nothing" > /dev/null ;; esac |
综合使用完整的脚本
到目前为止,您已经创建了一个脚本标头和一些简单变量,并且添加了一个函数,如清单 14 所示。
清单 14. 示例 Korn shell 脚本
$vi my_second_script.ksh #!/bin/ksh ################################################### # Written By: Jason Thomas # Purpose: This script was written to show users how to develop their first script # May 1, 2008 ################################################### #Define Variables HOME="/home/jthomas" #Simple home directory TIME=$(date +%H%M) # Set DATE equal to the output of running the shell command date HOSTNAME=$(hostname) # Set HOSTNAME equal to the output of the hostname command ################## function if_error ################## { if [[ $? -ne 0 ]]; then # check return code passed to function print "$1" # if rc > 0 then print error msg and quit exit $? fi } if [[ -e /tmp/file ]]; then #Check to see if the file exists first rm –rf /tmp/file #Delete file if_error "Error: Failed removing file /tmp/file" else print "/tmp/file doesn’t exist" fi if [[ -e /tmp/test ]]; then mkdir /tmp/test #Create the directory test if_error "Error: Failed trying to create directory /tmp/test" else print "Directory exists, no need to create directory" fi case $TIME in "2200") rm –rf /tmp/file1 ;; "2300") rm –rf /tmp/file1 ;; #End Script |
要运行这个脚本,您只需要键入 ./scriptname.ksh,如下所示:
$./my_second_script.ksh |
在命令行中将输入传递给某个脚本
您可以创建相应的脚本,以允许将输入传递到其中。请参见清单 15。
清单 15. 将输入传递到脚本中
#!/bin/ksh OPTION=$1 print "I love $OPTION" $./scriptname milk I love milk $./scriptname tea I love tea $./scriptname "peanut butter" I love peanut butter |
任何时候将信息传递到脚本中时,在脚本名之后的第一个选项称为 $1。在脚本名之后的第二个选项称为 $2,以此类推。这种编写脚本的方式非常好,因为这样一来,它更像是带有一些开关或者选项的 UNIX 命令。
在脚本中发送电子邮件
您可以使用脚本来生成某些类型的报告。例如,可能编写了某个脚本来对每天添加到系统中的新用户进行跟踪。这个脚本可以将输出写入到某个文件中,然后您可以将该文件发送给自己。通过这种方式,您可以获得每天添加到系统中的所有新用户的统计信息的副本。要实现这一点,可以运行下面的命令:
$REPORT="/tmp/users" cat $REPORT | mailx –s "User admin report from server XYZ" Jason_Thomas@kitzune |
这项操作将向您发送一封 $REPORT 文件内容的电子邮件。-s 将作为该电子邮件的主题。这项功能使用起来的确非常方便。
结束语
Korn Shell 脚本可以为您节约大量的时间,并使您的工作更加轻松。初看起来,它似乎令人生畏,但请记住,应该始终从简单之处入手,分别构建相应的脚本。请始终遵循同样的步骤:构建您的脚本标头,定义变量,然后对您的工作进行错误检查。您可能会发现,您希望为所进行的每项工作编写脚本。
(责任编辑:A6)