使用结构化命令

结构化命令(structured command)就是类似于 if-else 的条件执行语句。

if-then 语句

if-then 的格式如下:

if command
then
    commands
fi

在其他编程语言中,if 后面跟着一个 True 或 False 的逻辑表达式,但在 bash 中,先执行 if 后面的命令,如果该命令的退出状态码为 0(意味着成功执行),则执行 then 后面的语句。

有的脚本喜欢写成下面这样,看上去更像其他编程语言中的 if-then 语句:

if command; then
    commands
fi

下面是一个示例:

$ cat test9
#!/bin/bash
user=pi

if grep $user /etc/passwd
then
    echo "User $user exists"
    echo "This is the user's file"
    ls -a /home/$user/.b*
fi
$ /bin/bash test9
pi:x:1000:27:,,,:/home/pi:/bin/bash
cockpit-ws:x:114:121::/nonexisting:/usr/sbin/nologin
User pi exists
This is the user's file
/home/pi/.bash_history  /home/pi/.bash_logout  /home/pi/.bashrc

if语句使用 grep 命令在 /etc/passwd 文件中查找某个用户名当前是否在系统上使用,如果有用户使用了那个登录名,脚本会显示一些文本信息并列出用户 HOME 目录的 bash 文件。

if-then-else 语句

if-then-else 语句的格式如下:

if command
then
    commands
else
    commands
fi

我们可以在上一个脚本中加入 else 部分:

$ cat test9
#!/bin/bash
user=Todd

if grep $user /etc/passwd
then
    echo "User $user exists"
    echo "This is the user's file"
    ls -a /home/$user/.b*
else
    echo "User $user does not exist."
fi
$ /bin/bash test9
User Todd does not exist.

嵌套 if

if-then 可以嵌套在一起,当然这样看起来会很乱,这时可以用 elif 语句:

if command1
then
    commands
elif command2
then
    commands
elif command3
then
    commands
...
else
    commands
fi

最后的 else 不是必需的,可以省略。这里我懒得放例子了。

test 命令

if 只能测试退出状态码,如果需要进行数值比较,则需要利用 test 命令。test 的格式很简单:

test condition

如果 test 后面的条件为真,那么就会返回0,反之返回非零的退出状态码。比如:

$ test
$ echo $?
1
$ test ""
$ echo $?
1
$ test "0"
$ echo $?
0

如果 test 后面为空或空字符,那么退出状态码为 1;如果为非空字符,那么退出状态码为 0.

在 if-then 中,如果不想写 test,可以在 condition 两边用方括号 [ ] 括起来,注意括号与 condition 之间要有空格。如:

 [ ]
$ echo $?
1
$ [ "" ]
$ echo $?
1
$ [ "0" ]
$ echo $?
0

test 可以判断三类条件:

  • 数值比较
  • 字符串比较
  • 文件比较

数值比较

比较

描述

n1 -eq n2

检查n1 是否与n2 相等

n1 -ge n2

检查n1 是否大于或等于n2

n1 -gt n2

检查n1 是否大于n2

n1 -le n2

检查n1 是否小于或等于n2

n1 -lt n2

检查n1 是否小于n2

n1 -ne n2

检查n1 是否不等于n2

示例:

$ [ 1 -eq 1 ] ; echo $?
0
$ [ 1 -gt 2 ] ; echo $?
1
$ [ 1 -lt 2 ] ; echo $?
0
$ [ 1 -ne 2 ] ; echo $?
0
$ [ 1 -eq 1.1 ] ; echo $?
bash: [: 1.1: integer expression expected
2

注意 n1n2 必须是整数,否则就会像最后一行那样出错。

字符串比较

比较

描述

str1 = str2

检查str1 是否和str2 相同

str1 != str2

检查str1 是否和str2 不同

str1 < str2

检查str1 是否比str2

str1 > str2

检查str1 是否比str2

-n str1

检查str1 的长度是否非0

-z str1

检查str1 的长度是否为0

这里要注意的是比较大小:

  • 大于号和小于号必须转义,否则 shell 会把它们当作重定向符号,把字符串值当作文件名;
  • 大于和小于顺序和 sort 命令采取的不同,这里认为大写字母是小于小写字母的。

示例:

$ [ "Hello" \> "hello" ] ; echo $?
1
$ [ "Hello" \< "hello" ] ; echo $?
0

文件比较

比较

描述

-d file

检查file 是否存在并是一个目录

-e file

检查file 是否存在

-f file

检查file 是否存在并是一个文件

-r file

检查file 是否存在并可读

-s file

检查file 是否存在并非空

-w file

检查file 是否存在并可写

-x file

检查file 是否存在并可执行

-O file

检查file 是否存在并属当前用户所有

-G file

检查file 是否存在并且默认组与当前用户相同

file1 -nt file2

检查file1 是否比file2

file1 -ot file2

检查file1 是否比file2

这里我也懒得举例子了,以后用到再来看吧。

复合条件测试

我们可以用布尔运算符将两个条件组合在一起:

  • [ condition1 ] && [ condition2 ]
  • [ condition1 ] || [ condition2 ]

示例:

$ [ -d $HOME ] && [ -w $HOME/testing ] ; echo $?
1
$ [ -d $HOME ] || [ -w $HOME/testing ] ; echo $?
0

if-then 高级特性

使用双括号

test 和 [] 都只能使用简单的算术操作,双括号则提供了更多操作。除了 test 使用的标准数学运算符外,还有如下这些:

符号

描述

val++

后增

val--

后减

++val

先增

--val

先减

!

逻辑求反

~

位求反

**

幂运算

<<

左位移

>>

右位移

&

位布尔和

|

位布尔或

&&

逻辑和

||

逻辑或

而且双括号中的大于号无需转义。

$ cat test10
#!/bin/bash

val1=10

if (( $val1 ** 2 > 90))
then
   (( val2 = $val1 **2))
   echo "The square of $val1 is $val2"
fi
$ /bin/bash test10
The square of 10 is 100

case 命令

如果要匹配多个值,可以用 case,格式如下:

case variable in
pattern1 | pattern2) commands1;;
pattern3) commands2;;
*) default commands;;
esac

可以用 | 来匹配多个值,* 会捕获所有与已知模式不匹配的值。当执行完匹配的命令后,就会跳出 case。

$ cat test11
case $USER in
pi | root)
    echo "Welcome, $USER";;
testing)
    echo "Special testing account";;
Todd)
    echo "Hi, $USER";;
*)
    echo "Sorry, you are not allowed here";;
esac
$ /bin/bash test11
Welcome, pi