Zsh 语法

在大部分场景下,Shell 的速度比运行一个其他语言的程序要快,而且无需安装依赖,代码通常也更简洁,所以 Shell 脚本通常是更推荐的做法。

0状态码

Shell 的返回值通常只有两种,0非0。其中 0 表示正常返回,相当于 true非0 为异常返回,相当于 false。这个状态和一般的程序语言对 truefalse 的设定是相反的,值得注意。

计算表达式

执行

(( ... ))

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 简写

ifwhileuntil 语句中,仅当条件部分 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 ..."

所以如果数组中的元素有空格或者空元素,那么使用 "$*" 时得到的结果会发生变化,空元素会丢失,空格处会分隔成两个元素。

参考资料


© 2020