Bash 脚本的条件判断语法
if 语法
if
是最常用的条件判断结构, 只有符合给定条件时, 才会执行指定的命令。它的语法如下
单分支 if 语句
if 判断表达式; then
statement1
statement2
.......
fi
双分支的 if 语句
if 判断表达式; then
statement1
statement2
.....
else
statement3
statement4
fi
多分支的 if 语句
if 判断表达式1; then
statement1
elif 判断表达式2; then
statement2
elif 判断表达式3; then
statement3
else
statement4
fi
if
和 then
写在同一行时, 需要分号 (;
) 分隔。分号是 Bash 的命令分隔符。它们也可以写成两行, 这时不需要分号。
if true
then
echo 'hello world'
fi
除了多行的写法, if
结构也可以写成单行
$ if true; then echo 'hello world'; fi
hello world
注意, if
关键字后面也可以是一条命令, 该条命令执行成功 (返回值 0
), 就意味着判断条件成立。
$ if echo 'hi'; then echo 'hello world'; fi
hi
hello world
上面命令中, if
后面是一条命令echo 'hi'
。该命令会执行, 如果返回值是0
, 则执行then
的部分。
if
后面可以跟任意数量的命令。这时, 所有命令都会执行, 但是判断真假只看最后一个命令, 即使前面所有命令都失败, 只要最后一个命令返回 0
, 就会执行 then
的部分。
$ if false; true; then echo 'hello world'; fi
hello world
上面例子中, if
后面有两条命令 (false;true;
), 第二条命令 (true
) 决定了 then
的部分是否会执行。
test 命令
注意, 第二种和第三种写法, 括号两端必须要有空格
# 写法一
test expression
# 写法二
[ expression ]
# 写法三
[[ expression ]]
上面三种形式是等价的, 但是第三种形式还支持正则判断, 前两种不支持。
上面的 expression
是一个表达式。
这个表达式为真, test
命令执行成功 (返回值为 0
) 表达式为假, test
命令执行失败 (返回值为 1
)。
test -f /etc/hosts
echo $?
0
[ -f /etc/hosts ]
echo $?
0
上面的例子中, test
命令采用两种写法, 判断 /etc/hosts
文件是否存在, 这两种写法是等价的。命令执行后, 返回值为 0
, 表示该文件确实存在
例子
下面把 test
命令的三种形式, 用在 if
结构中, 判断一个文件是否存在。
# 写法一
if test -e /tmp/foo.txt ; then
echo "Found foo.txt"
fi
# 写法二
if [ -e /tmp/foo.txt ] ; then
echo "Found foo.txt"
fi
# 写法三
if [[ -e /tmp/foo.txt ]] ; then
echo "Found foo.txt"
fi
判断表达式
if
关键字后面, 跟的是一个命令。这个命令可以是 test
命令, 也可以是其他命令。命令的返回值为 0
表示判断成立, 否则表示不成立。因为这些命令主要是为了得到返回值, 所以可以视为表达式。
整数比较
[ integer1 -eq integer2 ] # integer1 等于 integer2, 则为真
[ integer1 -ne integer2 ] # integer1 不等于 integer2, 则为真
[ integer1 -gt integer2 ] # integer1 大于 integer2, 则为真
[ integer1 -lt integer2 ] # integer1 小于 integer2, 则为真
[ integer1 -ge integer2 ] # integer1 大于或等于 integer2, 则为真
[ integer1 -le integer2 ] # integer1 小于或等于 integer2, 则为真
例子
先判断变量 $INT
是否为空, 然后判断是否为 0
, 接着判断正负, 最后通过求余数判断奇偶
#!/bin/bash
INT=-5
if [ -z "$INT" ]; then
echo "INT is empty." >&2
exit 1
fi
if [ $INT -eq 0 ]; then
echo "INT is zero."
else
if [ $INT -lt 0 ]; then
echo "INT is negative."
else
echo "INT is positive."
fi
if [ $((INT % 2)) -eq 0 ]; then
echo "INT is even."
else
echo "INT is odd."
fi
fi
字符串比较
[ string ] # 如果 string 不为空 (长度大于 0), 则为真
[ -n string ] # 如果字符串 string 的长度大于零, 则为真
[ -z string ] # 如果字符串 string 的长度为零, 则为真
[ string1 = string2 ] # 如果 string1 和 string2 相同, 则为真
[ string1 == string2 ] # 等同于[ string1 = string2 ]
[ string1 != string2 ] # 如果 string1 和 string2 不相同, 则为真
[ string1 '>' string2 ] # 如果按照字典顺序 string1 排列在 string2 之后, 则为真
[ string1 '<' string2 ] # 如果按照字典顺序 string1 排列在 string2 之前, 则为真
[ string1 \> string2 ] # 用反斜杠转义
[ string1 \< string2 ] # 用反斜杠转义
注意, test
命令内部的 >
和 <
, 必须用引号引起来 (或者是用反斜杠转义)。否则, 它们会被 shell 解释为重定向操作符
例子
首先确定 $ANSWER
字符串是否为空。如果为空, 就终止脚本, 并把退出状态设为 1
。注意, 这里的 echo
命令把错误信息 There is no answer.
重定向到标准错误, 这是处理错误信息的常用方法。如果 $ANSWER
字符串不为空, 就判断它的值是否等于 yes
、no
或者 maybe
#!/bin/bash
ANSWER=maybe
if [ -z "$ANSWER" ]; then
echo "There is no answer." >&2
exit 1
fi
if [ "$ANSWER" = "yes" ]; then
echo "The answer is YES."
elif [ "$ANSWER" = "no" ]; then
echo "The answer is NO."
elif [ "$ANSWER" = "maybe" ]; then
echo "The answer is MAYBE."
else
echo "The answer is UNKNOWN."
fi
注意, 字符串判断时, 变量要放在双引号之中, 比如 [ -n "$COUNT" ]
, 否则变量替换成字符串以后, test
命令可能会报错, 提示参数过多。另外, 如果不放在双引号之中, 变量为空时, 命令会变成 [ -n ]
, 这时会判断为真。如果放在双引号之中, [ -n "" ]
就判断为假。
if 语句进行判断是否为空
这三种写法是等价的
[ "$name" = "" ]
[ ! "$name" ]
[ -z "$name" ]
逻辑运算符
通过逻辑运算, 可以把多个 test
判断表达式结合起来, 创造更复杂的判断。三种逻辑运算 AND
, OR
和 NOT
, 都有自己的专用符号。
AND 逻辑与: # 符号 &&, 也可使用参数 -a
OR 逻辑或: # 符号 ||, 也可使用参数 -o
NOT 逻辑非: # 符号 !
例子
下面是一个 AND
的例子, 判断整数是否在某个范围之内
&&
用来连接两个判断条件: 大于等于 $MIN_VAL
, 并且小于等于 $MAX_VAL
#!/bin/bash
MIN_VAL=1
MAX_VAL=100
INT=50
if [[ $INT -ge $MIN_VAL && $INT -le $MAX_VAL ]]; then
echo "$INT is within $MIN_VAL to $MAX_VAL."
else
echo "$INT is out of range."
fi
使用否定操作符 !
时, 最好用圆括号确定转义的范围
test
命令内部使用的圆括号, 必须使用引号或者反斜杠转义, 否则会被 Bash 解释。
if [ ! \( $INT -ge $MIN_VAL -a $INT -le $MAX_VAL \) ]; then
echo "$INT is outside $MIN_VAL to $MAX_VAL."
else
echo "$INT is in range."
fi
使用 -a
连接两个判断条件不太直观, 一般推荐使用 &&
代替, 上面的脚本可以改写成下面这样
if !([ $INT -ge $MIN_VAL ] && [ $INT -le $MAX_VAL ]); then
echo "$INT is outside $MIN_VAL to $MAX_VAL."
else
echo "$INT is in range."
fi
正则表达式
[[ expression ]]
这种判断形式, 支持正则表达式
[[ string1 =~ regex ]]
上面的语法中, regex
是一个正则表示式, =~
是正则比较运算符
例子
先判断变量 INT
的字符串形式, 是否满足 ^-?[0-9]+$
的正则模式, 如果满足就表明它是一个整数
#!/bin/bash
INT=-5
if [[ "$INT" =~ ^-?[0-9]+$ ]]; then
echo "INT is an integer."
exit 0
else
echo "INT is not an integer." >&2
exit 1
fi
文件测试
以下表达式用来判断文件状态
[ -a file ] # 如果 file 存在, 则为真
[ -b file ] # 如果 file 存在并且是一个块 (设备) 文件, 则为真
[ -c file ] # 如果 file 存在并且是一个字符 (设备) 文件, 则为真
[ -d file ] # 如果 file 存在并且是一个目录, 则为真
[ -e file ] # 如果 file 存在, 则为真
[ -f file ] # 如果 file 存在并且是一个普通文件, 则为真
[ -g file ] # 如果 file 存在并且设置了组 ID, 则为真
[ -G file ] # 如果 file 存在并且属于有效的组 ID, 则为真
[ -h file ] # 如果 file 存在并且是符号链接, 则为真
[ -k file ] # 如果 file 存在并且设置了它的 "sticky bit", 则为真
[ -L file ] # 如果 file 存在并且是一个符号链接, 则为真
[ -N file ] # 如果 file 存在并且自上次读取后已被修改, 则为真
[ -O file ] # 如果 file 存在并且属于有效的用户 ID, 则为真
[ -p file ] # 如果 file 存在并且是一个命名管道, 则为真
[ -r file ] # 如果 file 存在并且可读 (当前用户有可读权限), 则为真
[ -s file ] # 如果 file 存在且其长度大于零, 则为真
[ -S file ] # 如果 file 存在且是一个网络 socket, 则为真
[ -t fd ] # 如果 fd 是一个文件描述符, 并且重定向到终端, 则为真 这可以用来判断是否重定向了标准输入/输出/错误。
[ -u file ] # 如果 file 存在并且设置了 setuid 位, 则为真
[ -w file ] # 如果 file 存在并且可写 (当前用户拥有可写权限), 则为真
[ -x file ] # 如果 file 存在并且可执行 (当前用户有执行/搜索权限), 则为真
[ FILE1 -nt FILE2 ] # 如果 FILE1 比 FILE2 的更新时间更近, 或者 FILE1 存在而 FILE2 不存在, 则为真
[ FILE1 -ot FILE2 ] # 如果 FILE1 比 FILE2 的更新时间更旧, 或者 FILE2 存在而 FILE1 不存在, 则为真
[ FILE1 -ef FILE2 ] # 如果 FILE1 和 FILE2 引用相同的设备和 inode 编号, 则为真
例子
$FILE
要放在双引号之中, 这样可以防止变量 $FILE
为空, 从而出错。因为 $FILE
如果为空, 这时 [ -e $FILE ]
就变成 [ -e ]
, 这会被判断为真。而 $FILE
放在双引号之中, [ -e "$FILE" ]
就变成 [ -e "" ]
, 这会被判断为假。
#!/bin/bash
FILE=~/.bashrc
if [ -e "$FILE" ]; then
if [ -f "$FILE" ]; then
echo "$FILE is a regular file."
fi
if [ -d "$FILE" ]; then
echo "$FILE is a directory."
fi
if [ -r "$FILE" ]; then
echo "$FILE is readable."
fi
if [ -w "$FILE" ]; then
echo "$FILE is writable."
fi
if [ -x "$FILE" ]; then
echo "$FILE is executable/searchable."
fi
else
echo "$FILE does not exist"
exit 1
fi
算术判断
Bash 还提供了 ((...))
作为算术条件, 进行算术运算的判断
if ((3 > 2)); then
echo "true"
fi
注意, 算术判断不需要使用 test
命令, 而是直接使用 ((...))
结构。这个结构的返回值, 决定了判断的真假。
如果算术计算的结果是非零值, 则表示判断成立。这一点跟命令的返回值正好相反, 需要小心。
$ if ((1)); then echo "It is true."; fi
It is true.
$ if ((0)); then echo "It is true."; else echo "it is false."; fi
It is false.
上面例子中, ((1))
表示判断成立, ((0))
表示判断不成立。
算术条件 ((...))
也可以用于变量赋值, ((...))
中变量是可以不使用 $
来引用的
$ if (( foo = 5 ));then echo "foo is $foo"; fi
foo is 5
上面例子中, (( foo = 5 ))
完成了两件事情。首先把 5
赋值给变量 foo
, 然后根据返回值 5
, 判断条件为真。
注意, 赋值语句返回等号右边的值, 如果返回的是 0
, 则判断为假。
$ if (( foo = 0 ));then echo "It is true.";else echo "It is false."; fi
It is false.
下面是用算术条件改写的数值判断脚本
#!/bin/bash
INT=-5
if [[ "$INT" =~ ^-?[0-9]+$ ]]; then
if ((INT == 0)); then
echo "INT is zero."
else
if ((INT < 0)); then
echo "INT is negative."
else
echo "INT is positive."
fi
if (( ((INT % 2)) == 0)); then
echo "INT is even."
else
echo "INT is odd."
fi
fi
else
echo "INT is not an integer." >&2
exit 1
fi
只要是算术表达式, 都能用于 ((...))
语法
普通命令的逻辑运算
如果 if
结构使用的不是 test
命令, 而是普通命令, 比如上一节的 ((...))
算术运算, 或者 test
命令与普通命令混用, 那么可以使用 Bash 的命令控制操作符 &&
(AND) 和 ||
(OR), 进行多个命令的逻辑运算。
# 对于 && 操作符, 先执行 command1, 只有 command1 执行成功后, 才会执行 command2
command1 && command2
# 对于 || 操作符, 先执行 command1, 只有 command1 执行失败后, 才会执行 command2
command1 || command2
创建一个名为 temp
的目录, 执行成功后, 才会执行第二个命令, 进入这个目录。
mkdir temp && cd temp
测试目录 temp
是否存在, 如果不存在, 就会执行第二个命令, 创建这个目录。这种写法非常有助于在脚本中处理错误。
[ -d temp ] || mkdir temp
如果 temp
子目录不存在, 脚本会终止, 并且返回值为 1
[ ! -d temp ] && exit 1
if
与 &&
结合使用的写法
if [ condition ] && [ condition ]; then
command
fi
例子
只有在指定文件里面, 同时存在搜索词 word1
和 word2
, 就会执行 if
的命令部分。
#! /bin/bash
filename=$1
word1=$2
word2=$3
if grep $word1 $filename && grep $word2 $filename
then
echo "$word1 and $word2 are both in $filename."
fi
将一个 &&
判断表达式, 改写成对应的 if
结构
[[ -d "$dir_name" ]] && cd "$dir_name" && rm *
# 等同于
if [[ ! -d "$dir_name" ]]; then
echo "No such directory: '$dir_name'" >&2
exit 1
fi
if ! cd "$dir_name"; then
echo "Cannot cd to '$dir_name'" >&2
exit 1
fi
if ! rm *; then
echo "File deletion failed. Check results" >&2
exit 1
fi
case 结构
case
结构用于多值判断, 可以为每个值指定对应的命令, 跟包含多个 elif
的 if
结构等价。它的语法如下
expression
是一个表达式, pattern
是表达式的值或者一个模式, 可以有多条, 用来匹配多个值, 每条以两个分号 (;;
) 结尾
case expression in
pattern1 )
statement1 ;;
pattern2 )
statement2 ;;
...
esac
最后一条匹配语句的模式是 *
, 这个通配符可以匹配其他字符和没有输入字符的情况, 类似 if
的 else
部分
#!/bin/bash
echo -n "输入一个1到3之间的数字 (包含两端) > "
read character
case $character in
1 ) echo 1
;;
2 ) echo 2
;;
3 ) echo 3
;;
* ) echo 输入不符合要求
esac
case
的匹配模式可以使用各种通配符, 下面是一些例子
a)
: 匹配a
a|b)
: 匹配a
或b
[[:alpha:]])
: 匹配单个字母???)
: 匹配 3 个字符的单词*.txt)
: 匹配.txt
结尾*)
: 匹配任意输入, 通常作为case
结构的最后一个模式
使用通配符 [[:lower:]] | [[:upper:]]
匹配字母, [0-9]
匹配数字
#!/bin/bash
echo -n "输入一个字母或数字 > "
read character
case $character in
[[:lower:]] | [[:upper:]] ) echo "输入了字母 $character"
;;
[0-9] ) echo "输入了数字 $character"
;;
* ) echo "输入不符合要求"
esac
Bash 4.0 之前, case
结构只能匹配一个条件, 然后就会退出 case
结构。Bash 4.0 之后, 允许匹配多个条件, 这时可以用 ;;&
继续判断条件块。使用 bash --version
检查 Bash 版本
#!/bin/bash
# test.sh
read -n 1 -p "Type a character > "
echo
case $REPLY in
[[:upper:]]) echo "'$REPLY' is upper case." ;;&
[[:lower:]]) echo "'$REPLY' is lower case." ;;&
[[:alpha:]]) echo "'$REPLY' is alphabetic." ;;&
[[:digit:]]) echo "'$REPLY' is a digit." ;;&
[[:graph:]]) echo "'$REPLY' is a visible character." ;;&
[[:punct:]]) echo "'$REPLY' is a punctuation symbol." ;;&
[[:space:]]) echo "'$REPLY' is a whitespace character." ;;&
[[:xdigit:]]) echo "'$REPLY' is a hexadecimal digit." ;;&
esac
执行上面的脚本, 会得到下面的结果
$ test.sh
Type a character > a
'a' is lower case.
'a' is alphabetic.
'a' is a visible character.
'a' is a hexadecimal digit.
可以看到条件语句结尾添加了 ;;&
以后, 在匹配一个条件之后, 并没有退出 case
结构, 而是继续判断下一个条件
原文
shell中if语句的使用
条件判断 - Bash 脚本教程 - 网道
How do I check my bash version?