在大部分场景下,Shell 的速度比运行一个其他语言的程序要快,而且无需安装依赖,代码通常也更简洁,所以 Shell 脚本通常是更推荐的做法。
0状态码
Shell 的返回值通常只有两种,0
和 非0
。其中 0
表示正常返回,相当于 true
;非0
为异常返回,相当于 false
。这个状态和一般的程序语言对 true
和 false
的设定是相反的,值得注意。
计算表达式
执行
(( ... ))
let ...
官方术语叫 Arithmetic Evaluation。支持整数和浮点类型计算。(( ... ))
内部的代码被视为一个表达式,无需引号;let
后面应传一个表达式字符串,所以如果有空格或者其他分隔符,就需要加引号。
(( val = 1 + 2 ))
let val=1+2
let "val = 1 + 2"
替换
$(( ... ))
替换成内部表达式计算的结果。
echo $(( 1 + 2 ))
代码块
( list )
在 subshell 里执行一段代码,里面的变量不会暴露到外面来。
a=empty; ( a=hello; echo $a ); echo $a
{ list }
执行一段代码,和外部代码共用作用域。
a=empty; { a=hello; echo $a }; echo $a
条件
关于条件语法的更多细节可以查看这篇。
条件表达式
[[ exp ]]
执行一个条件表达式,如果值为 true
,则返回 0
,否则返回 非0
。
[[ $a -lt 1 ]] && echo "<1" || echo ">=1"
if / elif / else
if list then list [ elif list then list ] ... [ else list ] fi
if list { list } [ elif list { list } ] ... [ else { list } ]
注意每一个 list 部分都必须有明确的结尾,一般为换行或者 ;
,如果有括号 (( ... ))
或 [[ ... ]]
,则可以省略 ;
。
if true; then echo yes; fi
if ((1+2==3)); then echo yes; fi
if [[ -z "" ]]; then echo yes; fi
常用表达式
文件
- -s file - 文件存在且大小不为0
- -f file - 文件存在且为 regular file
字符串
- -n string - 字符串长度不为0
- -z string - 字符串长度为0
- string1 == string2 - 字符串相等
- string1 != string2 - 字符串不相等
- string1 < string2 - 字符串比较
- string1 > string2 - 字符串比较
- string =~ regexp - 字符串符合正则表达式
循环
for 循环遍历
for name ... [ in word ... ] term do list done
term 是换行或者 ;
,表示列表的结束。
for item in a b c; do echo $item; done
for item (a b c); do echo $item; done
array=(a b c); for item in $array; do echo $item; done
array=(a b c); for item ($array); do echo $item; done
for 循环计算
for (( [expr1] ; [expr2] ; [expr3] )) do list done
for ((i=0; i<3; i++)) do echo $i; done
while / until 循环
while list do list done
until list do list done
i=0; while (($i<3)); do ((i++)); echo $i; done
repeat 循环
repeat word do list done
i=0; repeat 3 do ((i++)); echo $i; done
函数
具名函数
function word ... [ () ] [ term ] { list } # DEPRECATED
word ... () [ term ] { list }
word ... () [ term ] command
Shell 中的函数只能返回 0
或者 非0
,表示是否正常退出,0
相当于 true
,其他值相当于 false
。
is_even() { return $(($1%2)); }
函数定义时可以指定输出的重定向,并且会保存下来。
is_even() { return $(($1%2)); } 2>&1
匿名函数
function { list } params
() { list } params
匿名函数是立即执行的。
() { echo Hello, $1; } Gerald
简写
很多情况下,then ... fi
/ do ... done
代码块都可以简写为 { ... }
,如果只有一个语句,甚至可以省略括号,如:
repeat 1 { echo yes }
但是 if / while / until
在使用简写的时候有一些额外的限制条件。
if / while / until
简写
在 if
、while
、until
语句中,仅当条件部分 list 有括号包裹时,如 (( ... ))
、[[ ... ]]
,才可以使用简写。
具体简写语法如下:
- 条件部分括号后面不能有
;
; - 执行的代码部分可以用大括号
{ ... }
包起来; - 如果只有一个语句,大括号也可以省略。
if ((1+2==3)) { echo yes; echo ok }
if [[ -z "" ]] echo yes
i=0; while (($i<3)) { ((i++)); echo $i; }
原因是条件部分也可以包含 { ... }
代码块,如果没有明确的界限,无法判断当前的代码块是哪一部分,如:
if true && { ok }; then echo yes; fi
参数
数组下标
默认情况下,数组的下标都是从 1
开始的,这一点和其他语言不一样。
@
和 *
在大部分情况下是一样的,仅当使用双引号包裹时有区别:
"$@"
等价于"$1" "$2" ...
"$*"
等价于"$1 $2 ..."
所以如果数组中的元素有空格或者空元素,那么使用 "$*"
时得到的结果会发生变化,空元素会丢失,空格处会分隔成两个元素。