所有的 UNIX® 用户都应该了解如何使用 Korn Shell 脚本。通过编写 Shell 脚本,可以让您实现许多任务的自动化,并可以为您节约大量的时间。初看起来,它似乎令人生畏,但只要遵循正确的指导,您就可以熟练地使用它。本文将指导您编写自己的 Korn Shell 脚本。

Kurt A. Riley, IT 顾问

Kurt Riley 从 2000 年起就开始从事 UNIX 和 Linux 系统方面的研究。他一直在为《财富》500 强中的许多公司提供咨询服务。Kurt 拥有信息技术学士学位,当前供职于惠而浦公司 (Whirlpool Corporation),从事 Tivoli 安全产品的支持工作。



2008 年 8 月 18 日

什么是 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 中,它们分别称为 STDINSTDOUTSTDERRSTDIN 指的是您可能从键盘获得的输入。STDOUT 是执行一个命令时打印到屏幕上的输出。STDERR 则对应于一个命令失败时的屏幕输出。STDINSTDOUTSTDERR 的文件描述符分别映射到数值 0、1 和 2。

如果希望检查一个命令成功或者失败,那么您可以进行类似清单 9 中的操作。

清单 9. 将输出重定向到 STDOUTSTDERR
$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 脚本可以为您节约大量的时间,并使您的工作更加轻松。初看起来,它似乎令人生畏,但请记住,应该始终从简单之处入手,分别构建相应的脚本。请始终遵循同样的步骤:构建您的脚本标头,定义变量,然后对您的工作进行错误检查。您可能会发现,您希望为所进行的每项工作编写脚本。

参考资料

学习

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文
  • 本专区的 Shell 编程专题 为您总结了在我们网站中相关 Shell 编程的一些精华内容,希望您能从中领略到 Shell 的强大功能,从而喜欢上它,使用上它。 。
  • 访问 UNIX 论坛,以获取免费的 UNIX 和 Linux 帮助以及 Internet 上的建议。
  • 查看 LINUX 期刊
  • 浏览技术书店,以了解有关这些技术主题及其他技术主题的相关书籍。
  • AIX and UNIX 专区:developerWorks 的“AIX and UNIX 专区”提供了大量与 AIX 系统管理的所有方面相关的信息,您可以利用它们来扩展自己的 UNIX 技能。
  • AIX and UNIX 新手入门:访问“AIX and UNIX 新手入门”页面可了解更多关于 AIX 和 UNIX 的内容。
  • AIX and UNIX 专题汇总:AIX and UNIX 专区已经为您推出了很多的技术专题,为您总结了很多热门的知识点。我们在后面还会继续推出很多相关的热门专题给您,为了方便您的访问,我们在这里为您把本专区的所有专题进行汇总,让您更方便的找到您需要的内容。
  • AIX Wiki:AIX 相关技术信息的协作环境。

获得产品和技术

  • 转到 KornShell 主页,以获取更多软件和信息。
  • 下载 IBM 产品评估版,获得来自 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 的应用程序开发工具和中间件产品。

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=AIX and UNIX
ArticleID=331360
ArticleTitle=Korn Shell 脚本入门
publish-date=08182008