ITEEDU

7.2. 更多if的高级使用方法

7.2.1. if/then/else结构

7.2.1.1. 虚构的例子

这是一个如果 if 命令测试为真,另外一个 if 测试是假而采取的一系列动作的结构。例子:

freddy scripts> gender="male"

freddy scripts> if [[ "$gender" == "f*" ]]
More input> then echo "Pleasure to meet you, Madame."
More input> else echo "How come the lady hasn't got a drink yet?"
More input> fi
How come the lady hasn't got a drink yet?

freddy scripts>

就像 CONSEQUENT-COMMANDS 跟在 then 语句后面一样,ALTERNATE-CONSEQUENT-COMMANDS跟在 else 后面并可以使用任何有返回状态的UNIX风格命令。

另外一个例子,从 第 7.1.2.1 节 “测试退出状态” 扩展开来:

anny ~> su -
Password:
[root@elegance root]# if ! grep ^$USER /etc/passwd 1> /dev/null
> then echo "your user account is not managed locally"
> else echo "your account is managed from the local /etc/passwd file"
> fi
your account is managed from the local /etc/passwd file
[root@elegance root]#

我们切换到 root 账号来证明 else 语句的效果- 你的 root 通常是一个本地账号,有时你自己的账号可能被一个特定的中央系统管理着,比如LDAP服务器。

7.2.1.2. 检查命令行参数

除了设置完参数然后运行脚本之外,通常更好的方法是通过命令行给变量设置值。

我们使用位置参数 $1$2,..., $N来达到此目的。 $#代表了命令行的参数数量, $0代表了脚本的名字。

以下是一个简单的例子:

图 7.1. 使用if来测试命令行参数

Simple if/then/else/fi construct: if [ "$1" == fish ]; then echo "Tux likes this"; else echo "Tux wants fish!"; fi

这里是另外一个例子,使用2个参数:

anny ~> cat weight.sh
#!/bin/bash

# This script prints a message about your weight if you give it your
# weight in kilos and hight in centimeters.

weight="$1"
height="$2"
idealweight=$[$height - 110]

if [ $weight -le $idealweight ] ; then
  echo "You should eat a bit more fat."
else
  echo "You should eat a bit more fruit."
fi

anny ~> bash -x weight.sh 55 169
+ weight=55
+ height=169
+ idealweight=59
+ '[' 55 -le 59 ']'
+ echo 'You should eat a bit more fat.'
You should eat a bit more fat.

7.2.1.3. 测试参数的数量

以下的例子显示了怎么改变之前的脚本如果参数少于或者多余2个来打印出一条消息:

anny ~> cat weight.sh
#!/bin/bash

# This script prints a message about your weight if you give it your
# weight in kilos and hight in centimeters.

if [ ! $# == 2 ]; then
  echo "Usage: $0 weight_in_kilos length_in_centimeters"
  exit
fi

weight="$1"
height="$2"
idealweight=$[$height - 110]

if [ $weight -le $idealweight ] ; then
  echo "You should eat a bit more fat."
else
  echo "You should eat a bit more fruit."
fi

anny ~> weight.sh 70 150
You should eat a bit more fruit.

anny ~> weight.sh 70 150 33
Usage: ./weight.sh weight_in_kilos length_in_centimeters

第一个参数代表$1,第二个参数代表$2,以此类推,参数数量的总数存在$#中。

查阅 第 7.2.5 节 “使用exit语句和if” 来得到更好的打印消息的方法。

7.2.1.4. 测试一个存在的文件

在许多脚本当中这个测试都成功,因为如果你知道某些功能不工作那运行很多程序也是没有用的:

#!/bin/bash

# This script gives information about a file.

FILENAME="$1"

echo "Properties for $FILENAME:"

if [ -f $FILENAME ]; then
  echo "Size is $(ls -lh $FILENAME | awk '{ print $5 }')"
  echo "Type is $(file $FILENAME | cut -d":" -f2 -)"
  echo "Inode number is $(ls -i $FILENAME | cut -d" " -f1 -)"
  echo "$(df -h $FILENAME | grep -v Mounted | awk '{ print "On",$1", \
which is mounted as the",$6,"partition."}')"
else
  echo "File does not exist."
fi

注意文件是使用变量来指向的;在这个例子中它是脚本的第一个参数。另外,当没有提供任何参数的时候,文件的存放位置通常存储在脚本开始处的变量里,他们的内容是依赖于使用的那些变量。因此,当你想在一个脚本中改变文件的名字,你只要做一次。

7.2.2. if/then/elif/else结构

7.2.2.1. 概要

这是 if 语句的完全形式:

if TEST-COMMANDS; then

CONSEQUENT-COMMANDS;

elif MORE-TEST-COMMANDS; then

MORE-CONSEQUENT-COMMANDS;

else ALTERNATE-CONSEQUENT-COMMANDS;

fi

TEST-COMMANDS 内容执行完后,如果它的返回状态是零,那么就执行 CONSEQUENT-COMMANDS 。如果 TEST-COMMANDS 返回一个非零状态,每个 elif 依次执行,相应的 MORE-CONSEQUENT-COMMANDS 就执行,然后命令结束。如果 else 跟在一个 ALTERNATE-CONSEQUENT-COMMANDS 内容之后,而且在最好的 if 或者 elif 子句的最后命令有一个非零状态,那么 ALTERNATE-CONSEQUENT-COMMANDS 就执行。返回状态就是最后执行的命令的退出状态或者没有条件测试成功就是零。

7.2.2.2. 例子

这是一个你可以把它放到crontab来每天执行的例子:

anny /etc/cron.daily> cat disktest.sh
#!/bin/bash

# This script does a very simple test for checking disk space.

space=`df -h | awk '{print $5}' | grep % | grep -v Use | sort -n | tail -1 | cut -d "%" -f1 -`
alertvalue="80"

if [ "$space" -ge "$alertvalue" ]; then
  echo "At least one of my disks is nearly full!" | mail -s "daily diskcheck" root
else
  echo "Disk space normal" | mail -s "daily diskcheck" root
fi

7.2.3. if嵌套语句

if 语句里面,你可以使用另外一个 if 语句。只要你能逻辑管理你就可以使用多层嵌套。

以下是一个测试闰年的例子:

anny ~/testdir> cat testleap.sh
#!/bin/bash
# This script will test if we're in a leap year or not.

year=`date +%Y`

if [ $[$year % 400] -eq "0" ]; then
  echo "This is a leap year.  February has 29 days."
elif [ $[$year % 4] -eq 0 ]; then
        if [ $[$year % 100] -ne 0 ]; then
          echo "This is a leap year, February has 29 days."
        else
          echo "This is not a leap year.  February has 28 days."
        fi
else
  echo "This is not a leap year.  February has 28 days."
fi

anny ~/testdir> date
Tue Jan 14 20:37:55 CET 2003

anny ~/testdir> testleap.sh
This is not a leap year.

7.2.4. 布尔操作

以上的脚本可以用布尔操作符 “AND(&&)” 和 “OR(||)” 来缩短。

图 7.2. 使用布尔操作符的例子

year=`date +%Y`; if (( ("$year" % 400) == "0" )) || (( ("$year" % 4 == "0") && ("$year" % 100 != "0") )); then echo "this is a leap year."; else echo "not a leap year"; fi

我们使用双括号来测试一个数学表达式,见 第 3.4.6 节 “算术扩展”。和使用 let 语句是一样的。如果使用类似 $[$year % 400],可能你会对尖括号的使用感到迷惑,因为在这里,尖括号它们自己并不代表一个实际的命令。

在其他一些编辑器中, gvim 是根据文件格式来进行色彩显示的其中之一;这些编辑器在发现代码中的错误时候非常有用。

7.2.5. 使用exit语句和if

我们已经在 第 7.2.1.3 节 “测试参数的数量” 简要的看到了 exit 语句。它使整个脚本中止运行。最常使用于来自用户输入的不正确请求,比如一条语句没有成功运行或者某些其他错误发生。

exit 语句可以带一个可选参数。参数是一个整数退出状态码,储存在 $? 中的返回给父进程的退出状态码。

0参数意味着脚本成功运行完毕。程序员会用其他值来给父进程传递不同的消息,所以根据子进程的成功或者失败,父进程采取不同的动作。如果没有参数给 exit 语句,父shell使用 $? 变量的现存值。

下面是一个和 penguin.sh 脚本相似的例子,会对 feed.sh 传回一个退出状态:

anny ~/testdir> cat penguin.sh
#!/bin/bash
                                                                                                 
# This script lets you present different menus to Tux.  He will only be happy
# when given a fish.  We've also added a dolphin and (presumably) a camel.
                                                                                                 
if [ "$menu" == "fish" ]; then
  if [ "$animal" == "penguin" ]; then
    echo "Hmmmmmm fish... Tux happy!"
  elif [ "$animal" == "dolphin" ]; then
    echo "Pweetpeettreetppeterdepweet!"
  else
    echo "*prrrrrrrt*"
  fi
else
  if [ "$animal" == "penguin" ]; then
    echo "Tux don't like that.  Tux wants fish!"
    exit 1
  elif [ "$animal" == "dolphin" ]; then
    echo "Pweepwishpeeterdepweet!"
    exit 2
  else
    echo "Will you read this sign?!"
    exit 3
  fi
fi

这个脚本被下面那个输出变量 menuanimal 的脚本调用:

anny ~/testdir> cat feed.sh
#!/bin/bash
# This script acts upon the exit status given by penguin.sh
                                                                                                 
export menu="$1"
export animal="$2"
                                                                                                 
feed="/nethome/anny/testdir/penguin.sh"
                                                                                                 
$feed $menu $animal
                                                                                                 
case $? in
                                                                                                 
1)
  echo "Guard: You'd better give'm a fish, less they get violent..."
  ;;
2)
  echo "Guard: It's because of people like you that they are leaving earth all the time..."
  ;;
3)
  echo "Guard: Buy the food that the Zoo provides for the animals, you ***, how
do you think we survive?"
  ;;
*)
  echo "Guard: Don't forget the guide!"
  ;;
esac
                                                                                                 
anny ~/testdir> ./feed.sh apple penguin
Tux don't like that.  Tux wants fish!
Guard: You'd better give'm a fish, less they get violent...

就像你看到的,退出状态码可以自由选择。退出命令通常有一系列预定义码;请见程序员手册得到每个命令的更多信息。