最近在看 deno,发现它的安装脚本写的非常简洁易懂,作为一名前端,之前一直对 shell 脚本半懂不懂、似懂非懂,但是自从看了 deno 的脚本之后,突然觉得写 shell 逻辑也可以这么舒服,里面基本上都是使用简单的 if 语句来实现全部功能的。
任何一门编程语言中都有 if 条件判断语句,在 shell 脚本编程中,自然也少不了这个场景,例如:
- 判断目录是否存在
- 判断命令是否执行成功
- 判断文件是否可写
- ……
shell 脚本中,条件判断语句的基本结构是:
if command
then
commands
fi
但很多人喜欢下面的写法,因为看起来更像高级编程语言的写法:
if command; then
commands
fi
在本文的代码中,也采用这种写法。一般来讲,if 后面的语句是一个布尔类型的值,即:TRUE 或者 FALSE,但是在 shell 中却并非如此,而是一个命令,例如:
if pwd; then
echo "run success"
fi
shell 中的 if 语句会执行 if 后面的命令,如果该命令的退出状态码为 0(表示命令成功运行),那么 then 后面的命令就会被执行。
那在 shell 中,怎么做数值比较(例如 5>4),或者字符串比较(例如 'a' > 'A'),或者目录是否存在这样判断呢?别急,本文就是讲这个的,在 shell 中,叫做 test 命令。
test 命令
test 命令有两种语法,第一种是:
if test condition; then
commands
fi
第二种是:
if [ condition ]; then
commands
fi
这里尤其需要注意的是:第一个方括号里面必须要用空格,否则会报错!即:
[的后面必须要有空格,即:[]的前面必须要有空格,即:]
接下来,我们来学习一些常见的判断场景。
数值比较
在 JavaScript 中,我们经常会这样写:
if (count > 0) {
// do something
}
判断变量 count 和数值 0 的大小,在 shell 中的写法是这样的:
if [ $value -gt 0 ]; then
# do something
fi
即环境变量和数字 0 的比较,在 deno 的安装脚本中就有数值比较的经典案例:
接下来上机验证一下:
$ value=3
$ if [ $value -gt 0 ]; then
echo "$value is than 0"
fi
3 is than 0
可以看到,输出了正确的结果。常用的语法:
| 语法 | 解释 | 助记单词 |
|---|---|---|
| n1 -eq n2 | n1 是否等于 n2 | equal to |
| n1 -ne n2 | n1 是否不等于 n2 | not equal |
| n1 -ge n2 | n1 是否大于等于 n2 | greater than or equal |
| n1 -gt n2 | n1 是否大于 n2 | greater than |
| n1 -le n2 | n1 是否小于等于 n2 | less than or equal |
| n1 -lt n2 | n1 是否小于 n2 | less than |
但是非常遗憾的是,这种方式只能比较整数,如果是浮点数,例如 3.5 的话,就会报错:
$ value=3.5
$ if [ $value -gt 0 ]; then
echo "$value is than 0"
fi
[: integer expression expected: 3.5
你可能会想,shell 脚本编程也太弱鸡了吧,别慌,还有更好的数值比较方法,即双括号语法:
if (( expression )); then
# do something
fi
在双括号里面,可以直接写数学表达式,例如加减乘除等等,我们来验证一下:
$ value=3.5
$ if (( $value > 0 )); then
echo "$value is than 0"
fi
3.5 is than 0
能够正确处理浮点数!还可以处理更复杂的运算,例如:
$ value=3.5
$ if (( $value * 2 + 1 == 8 )); then
echo "result is 8"
fi
result is 8
怎么样?有了双括号,是不是进行数值比较更简单、更方便了呢?
字符串比较
上一节中讲到,数值比较可以在条件表达式中用 -eq、-gt 这种语法,如果是字符串的话,则不能这么写,要用 >、=、< 这种:
if [ $value = hello ]; then
# do something
fi
在 deno 的安装脚本中就有字符串比较的经典案例:
如果当前操作系统是 Windows 的话,就设置 target 的值。我们上机实验一下:
$ value=hello
$ if [ $value = hello ]; then
echo "hello"
fi
hello
比较数字用 -eq,比较字符串用 = ,这种设计还蛮反直觉的,而且如果在 [ ] 中用 > 和 < 比较字符串的话,需要用反斜线转义,不能直接写,例如:
if [ $value1 > $value2 ]; then
echo $value1 greater than $value2
fi
这样写不会报错,但是结果是错误的,因为在这里 > 表示重定向,而不是比较大小,需要这么写:
if [ $value1 \> $value2 ]; then
echo $value1 greater than $value2
fi
这也太恶心了,太不优雅了吧。是的,不止你一个人这么觉得,大家都这么认为,于是就有了另外一种比较字符串的语法,即双方括号语法:
if [[ $value1 > $value2 ]]; then
echo $value1 greater than $value2
fi
这样子看起来就舒服多啦,双小括号表示数值比较,双中括号表示字符串比较,简单易懂。关键双中括号还提供了强大的模式匹配功能:
if [[ $value == hello* ]]; then
echo $value1 prefix is hello
fi
这样只要 value 的值是以 hello 为前缀的,就都符合条件。
文件比较
讲完了数值比较和字符串比较,在 shell 编程中,最实用的其实是文件比较,它允许用户测试系统中文件和目录的状态,例如:
$ directory=/home/keliq
$ if [ -d $directory ]; then
echo directory exits
fi
在 deno 的安装脚本中就有文件比较的经典案例:
如果目录不存在,就创建该目录,-d 可以用于检查目录是否存在,-f 可以用于检查文件是否存在,如果不区分目录或文件,则可以用 -e,常见的语法如下:
| 语法 | 含义 |
|---|---|
| -e file | 检查 file 是否存在 |
| -d file | 检查目录 file 是否存在 |
| -f file | 检查文件 file 是否存在 |
| -r file | 检查 file 是否只读 |
| -s file | 检查 file 是否非空 |
| -w file | 检查 file 是否可写 |
| -x file | 检查 file 是否可执行 |
| file1 -nt file2 | 检查 file1 是否比 file2 新 |
| file1 -ot file2 | 检查 file1 是否比 file2 旧 |
总结
到这里,我相信 shell 中的 if 语句大家都已经能够掌握了,口诀:
- 数值比较双圆括号
- 字符串比较双方括号
- 文件比较杠def