Chinaunix首页 | 论坛 | 博客
  • 博客访问: 522274
  • 博文数量: 134
  • 博客积分: 7990
  • 博客等级: 少将
  • 技术积分: 1290
  • 用 户 组: 普通用户
  • 注册时间: 2007-10-29 11:43
文章分类

全部博文(134)

文章存档

2009年(7)

2008年(80)

2007年(47)

我的朋友

分类:

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 中,它们分别称为 STDINSTDOUTSTDERRSTDIN 指的是您可能从键盘获得的输入。STDOUT 是执行一个命令时打印到屏幕上的输出。STDERR 则对应于一个命令失败时的屏幕输出。STDINSTDOUTSTDERR 的文件描述符分别映射到数值 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 脚本可以为您节约大量的时间,并使您的工作更加轻松。初看起来,它似乎令人生畏,但请记住,应该始终从简单之处入手,分别构建相应的脚本。请始终遵循同样的步骤:构建您的脚本标头,定义变量,然后对您的工作进行错误检查。您可能会发现,您希望为所进行的每项工作编写脚本。

阅读(2388) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~