Section 22.1: 文件类型测试

-e 条件运算符测试文件是否存在(包括所有文件类型:目录等)。

if [[ -e $filename ]]; then
    echo "$filename exists"
fi

还有针对特定文件类型的测试。

if [[ -f $filename ]]; then
    echo "$filename is a regular file"
elif [[ -d $filename ]]; then
    echo "$filename is a directory"
elif [[ -p $filename ]]; then
    echo "$filename is a named pipe"
elif [[ -S $filename ]]; then
    echo "$filename is a named socket"
elif [[ -b $filename ]]; then
    echo "$filename is a block device"
elif [[ -c $filename ]]; then
    echo "$filename is a character device"
fi
if [[ -L $filename ]]; then
    echo "$filename is a symbolic link (to any file type)"
fi

对于符号链接,除了 -L 之外,以下这些测试也适用于返回 false 的失效链接。

if [[ -L $filename || -e $filename ]]; then
echo "$filename exists (but may be a broken symbolic link)"
fi
if [[ -L $filename && ! -e $filename ]]; then
echo "$filename is a broken symbolic link"
fi

Section 22.2: 字符串比较和匹配

字符串比较在带引号的字符串之间使用 == 运算符。!= 运算符否定比较。

if [[ "$string1" == "$string2" ]]; then
    echo "\$string1 and \$string2 are identical"
fi
if [[ "$string1" != "$string2" ]]; then
    echo "\$string1 and \$string2 are not identical"
fi

如果右侧未引用,则它是 $string1 匹配的通配符模式。

string='abc'
pattern1='a*'
pattern2='x*'
if [[ "$string" == $pattern1 ]]; then
    # the test is true
    echo "The string $string matches the pattern $pattern"
fi
if [[ "$string" != $pattern2 ]]; then
    # the test is false
    echo "The string $string does not match the pattern $pattern"
fi

'<' 和 '>' 运算符按字典顺序比较字符串(字符串没有小于或等于或大于或等于的运算符)。

空字符串的测试。

if [[ -n "$string" ]]; then
    echo "$string is non-empty"
fi
if [[ -z "${string// }" ]]; then
    echo "$string is empty or contains only spaces"
fi
if [[ -z "$string" ]]; then
    echo "$string is empty"
fi

在上面,'-z' 检查可能意味着 '$string' 未设置,或者它被设置为空字符串。要区分 empty 和 unset,请使用:

if [[ -n "${string+x}" ]]; then
    echo "$string is set, possibly to the empty string"
fi
if [[ -n "${string-x}" ]]; then
    echo "$string is either unset or set to a non-empty string"
fi
if [[ -z "${string+x}" ]]; then
    echo "$string is unset"
fi
if [[ -z "${string-x}" ]]; then
    echo "$string is set to an empty string"
fi

其中 x 是任意的。或以表格形式显示:

+-------+-------+-----------+
$string is: | unset | empty | non-empty |
+-----------------------+-------+-------+-----------+
| [[ -z ${string} ]] | true | true | false |
| [[ -z ${string+x} ]] | true | false | false |
| [[ -z ${string-x} ]] | false | true | false |
| [[ -n ${string} ]] | false | false | true |
| [[ -n ${string+x} ]] | false | true | true |
| [[ -n ${string-x} ]] | true | false | true |
+-----------------------+-------+-------+-----------+

或者,可以在 case 语句中检查状态:

case ${var+x$var} in
    (x) echo empty;;
    ("") echo unset;;
    (x*[![:blank:]]*) echo non-blank;;
    (*) echo blank
esac

其中 [:blank:] 是特定于语言环境的水平间距字符(制表符、空格等)。

Section 22.3: 测试命令的退出状态

退出状态 0:成功

退出状态非 0:失败

要测试一个命令的退出状态可以用下面的例子:

if command;then
    echo 'success'
else
    echo 'failure'
fi

Section 22.4: 单行测试

您可以执行以下操作:

[[ $s = 'something' ]] && echo 'matched' || echo "didn't match"
[[ $s == 'something' ]] && echo 'matched' || echo "didn't match"
[[ $s != 'something' ]] && echo "didn't match" || echo "matched"
[[ $s -eq 10 ]] && echo 'equal' || echo "not equal"
(( $s == 10 )) && echo 'equal' || echo 'not equal'

单行测试的返回状态:

command && echo 'exited with 0' || echo 'non 0 exit'
cmd && cmd1 && echo 'previous cmds were successful' || echo 'one of them failed'
cmd || cmd1 #If cmd fails try cmd1

Section 22.5: 文件比较

if [[ $file1 -ef $file2 ]]; then
    echo "$file1 and $file2 are the same file"
fi

“同一文件” 意味着就地修改其中一个文件会影响另一个文件。两个文件可以是相同的,即使它们具有不同的名称,例如,如果它们是硬链接,或者它们是具有相同目标的符号链接,或者如果一个是指向另一个的符号链接。

如果两个文件的内容相同,但它们是不同的文件(因此修改一个文件不会影响另一个文件),那么 '-ef' 报告它们不同。如果要逐字节比较两个文件,请使用 cmp 实用程序。

if cmp -s -- "$file1" "$file2"; then
    echo "$file1 and $file2 have identical contents"
else
    echo "$file1 and $file2 differ"
fi

要生成文本文件之间差异的可读列表,请使用 diff 实用程序。

if diff -u "$file1" "$file2"; then
    echo "$file1 and $file2 have identical contents"
else
    : # the differences between the files have been listed
fi

Section 22.6: File access tests

if [[ -r $filename ]]; then
    echo "$filename is a readable file"
fi
if [[ -w $filename ]]; then
    echo "$filename is a writable file"
fi
if [[ -x $filename ]]; then
    echo "$filename is an executable file"
fi

这些测试会考虑权限和所有权,以确定脚本(或从脚本启动的程序)是否可以访问该文件。

小心争用条件 (TOCTOU):仅仅因为测试现在成功并不意味着它在下一行仍然有效。通常最好尝试访问文件并处理错误,而不是先测试然后再处理错误,以防文件在此期间发生更改。

Section 22.7: 数值比较

数值比较使用 -eq 运算符会比较友好。

if [[ $num1 -eq $num2 ]]; then
    echo "$num1 == $num2"
fi
if [[ $num1 -le $num2 ]]; then
    echo "$num1 <= $num2"
fi

有六个数字运算符:

  • -eq 等于

  • -ne 不等于

  • -le 小于等于

  • -lt 小于

  • -ge 大于等于

  • -gt 大于

要记得的是,[[ ... ]] 中的 < 和 > 运算符比较的是字符串,而不是数字。

if [[ 9 -lt 10 ]]; then
    echo "9 is before 10 in numeric order"
fi
if [[ 9 > 10 ]]; then
    echo "9 is after 10 in lexicographic order"
fi

The two sides must be numbers written in decimal (or in octal with a leading zero). Alternatively, use the ((…)) arithmetic expression syntax, which performs integer calculations in a C/Java/…-like syntax.

两边必须是以十进制形式书写的数字(或前导为零的八进制形式)。或者,使用 ((...)) 算术表达式语法,该语法以类似 C/Java/...-的语法执行整数计算。

x=2
if ((2*x == 4)); then
echo "2 times 2 is 4"
fi
((x += 1))
echo "2 plus 1 is $x"