分类:
2008-08-19 09:26:48
所有的 UNIX® 用户都应该了解如何使用 Korn Shell 脚本。通过编写 Shell 脚本,可以让您实现许多任务的自动化,并可以为您节约大量的时间。初看起来,它似乎令人生畏,但只要遵循正确的指导,您就可以熟练地使用它。本文将指导您编写自己的 Korn Shell 脚本。
IBM® AIX® 操作系统和其他的类 UNIX 操作系统一样,都需要通过某种方式与内核进行通信。这项任务正是通过使用 Shell 来实现的。您可以使用各种不同的 Shell,但本文重点关注于 Korn Shell。Korn Shell 是 AIX 所使用的缺省 Shell。
当您登录到 AIX 中时,将以某个目录的提示符作为开始。缺省目录通常是您的 home 目录。之所以将其称为 home 目录,是因为该目录的结构通常如下所示:
$/home/jthomas: |
当登录时,您将处于命令行或者命令提示符处。这正是您输入 UNIX 命令的地方。您可以输入与 UNIX 内核进行交互的 Shell 命令。这些命令可能简单到只有一行(比如查看日期),也可能为多行,而这取决于您所进行的操作。清单 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 脚本中的第一行是 Shell 自身。它被表示为下面的形式:
#!/bin/ksh |
要在 AIX 中编写 Korn Shell 脚本,您需要使用一种文本编辑器。vi 是一种使用最广泛、且随处可见的文本编辑器。开始接触时可能会觉得有点麻烦,但随着使用 vi 的次数的增多,您将熟练地掌握它。关于如何使用 vi 文本编辑器,人们撰写了很多相关的书籍。
要开始编写您的第一个 Korn Shell 脚本,首先需要打开 vi 编辑器,并添加 Shell 名称作为第一行。完成这项操作之后,您需要构建某种类型的脚本标头,用来告诉编写脚本的用户,该脚本将执行什么操作,以及该脚本的编写时间。您可以对脚本进行任意地命名,但通常使用扩展名 .ksh 来表示 Korn Shell 脚本。您并不是必须要这样做,但这是一种很好的做法。镑符号 (#) 可用于对脚本进行注释,如清单 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 所示,但您不需要这样做。
#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 代码了。
让我们开始从某个文件中读取一些行。在这个示例中,使用您已经在脚本中定义过的 /etc/passwd 文件,并且仅打印用户名称,如清单 4 中所示。
$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 中所示。
$./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 显示了用来检查该文件是否存在的语法。
#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 命令时查看 $?
变量的设置。
$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 显示了如何在脚本中使用 $?
。
#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 中的操作。
$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 所示。
################## 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 所示。
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
语句是另一种条件型语句,您可以用该语句来替代 if
语句。case
语句以 case
开头,并且以反写的 (esac
) 结尾。当您的脚本变得比较复杂、并且需要执行不同的任务时,可以使用 case
语句迅速地进行构建。清单 12 提供了一个示例。
case value in "Mypattern") commands to execute when value matches Mypattern ;; esac |
假设您希望在某天中不同的时刻删除一个文件。那么您可以创建一个变量,用于检查具体的时刻:
TIME=$(date +%H%M) |
清单 13 中所显示的代码将在晚上 10:00 和晚上 11:00 删除某个文件。因此,每次执行这个代码段的时候,将检查 $TIME
是否匹配 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 所示。
$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。
#!/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 脚本可以为您节约大量的时间,并使您的工作更加轻松。初看起来,它似乎令人生畏,但请记住,应该始终从简单之处入手,分别构建相应的脚本。请始终遵循同样的步骤:构建您的脚本标头,定义变量,然后对您的工作进行错误检查。您可能会发现,您希望为所进行的每项工作编写脚本。