zsh-变量和语句

文章目录
  1. 1. 格式约定
  2. 2. 变量
    1. 2.1. 变量定义
    2. 2.2. 变量比较
  3. 3. 语句
    1. 3.1. 条件语句
    2. 3.2. 循环语句
    3. 3.3. 分支语句
    4. 3.4. 用户输入选择语句
    5. 3.5. 异常处理语句
    6. 3.6. 简化的条件语句
  4. 4. 参考

格式约定

文中行首的 % 代表 zsh 的命令提示符(类似 bash 的 $,这个是可以自由定义的,具体是什么不重要),行首的 > 代表此行是换行后的输入内容,以 # 开头的为注释(非 root 用户的命令提示符,本系列文章不需要 root 用户),其余的是命令的输出内容。另外某些地方会贴成段的 zsh 代码,那样就省略开头的 %,比较容易分辨。

变量

接触一门新的编程语言,运行完 Hello World 后,首先要了解的基本就是如何定义和使用变量了。有了变量后可以比较变量内容,进而可以接触条件、循环、分支等语句,继而了解函数的用法,更高级的数据结构的使用,更多库函数,等等。这样就大概了解了一门面向过程的语言的基本用法,剩下的可以等到用的时候再查手册。

所以这一篇讲最基本的变量和语句。

zsh 有 5 种变量:整数、浮点数(bash 不支持)、字符串、数组、哈希表(或者叫关联数组或者字典,本系列文章统一使用“哈希表”这一名词),另外还有一些其他语言少有的东西,比如 alias(但主要是交互时使用,编程时基本用不到)。此篇只涉及整数、浮点数、字符串,并且不涉及数值计算和字符串处理等内容。

变量定义

Zsh 的变量多数情况不需要提前声明或者指定类型,可以直接赋值和使用(但哈希表是一个例外)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 等号两端不能有空格
% num1=123
% num2=123.456
% str1=abcde
# 如果字符串中包含空格等特殊字符,需要加引号
% str2='abc def'
# 也可以用双引号,但和单引号有区别,比如双引号里可以使用变量,而单引号不可以
% str3="abc def $num1"
# 在字符串中可以使用转义字符,单双引号均可
% str4="abc\tdef\ng"
# 输出变量,也可以使用 print
% echo $str1
abcde
# 简单的数值计算
% num3=$(($num1 + $num2))
# (( 中的变量名可以不用 $
% num3=$((num1 + num2))
# 简单的字符串操作
% str=abcdef
# 2 和 4 都是字符在数组的位置,从 1 开始数,逗号两边不能有空格
% echo $str[2,4]
bcd
# -1 是最后一个字符
% echo $str[4,-1]
def

变量比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 比较数值
% num=123
# (( )) 用于数值比较等操作,如果为真返回 0,否则返回 1
# && 后边的语句在前边的语句为真时才执行
# 注意这里只能使用双等号来比较
% ((num == 123)) && echo good
good
# (( 里边可以使用与(&&)或(||)非(!)操作符,同 c 系列语言
% ((num == 1 || num == 2)) && echo good
# 比较字符串
% str=abc
# 比较字符串要用 [[,内侧要有空格,[[ 的具体用法之后会讲到
# 这里双等号可以替换成单等号,可以根据自己的习惯选用
# 本系列文章统一使用双等号,因为和 (( )) 一致,并且使用双等号的常用编程语言更多些
# $str 两侧不需要加双引号,即使 str 未定义或者 $str 中含空格和特殊符号
% [[ $str == abc ]] && echo good
good
# 可以和空字符串 "" 比较,未定义的字符串和空字符串比较结果为真
# [[ 里也可以用 && || !
% [[ $str == "" || $str == 123 ]] && echo good

语句

稍微了解下简单变量的使用后,快速进入语句部分。

zsh 支持多种风格的语法,包括经典的 posix sh (bash 的语法和它类似,但有一些扩展,可以归为一类)的,以及 csh 风格的等等。但 posix sh 的语法并不好用,我们没必要一定使用这个。我只选用一种我认为最方便简洁的语法,没有 fithendodoneesacin 等的关键字(虽然其中某些关键字其他编程语言也有,但基本用法都各异,而且容易混淆),也不需要多余的分号。如果不确定语法是否符合预期,可以定义一个函数然后使用 which 查看,内容会被转化成原始(posix sh 风格)的样子。熟悉 bash 并且喜欢使用 bash 语法的读者可以跳过这部分内容,语法的不同并不影响后续内容的阅读,继续使用 bash 风格语法写 zsh 也是没有问题的。

条件语句

1
2
3
4
5
# 格式
if [[ ]] {
} elif {
} else {
}

大括号也可以另起一行,本系列文章统一使用这种风格,缩进为 4 个空格。注意 elif 不可写作 else if

[[ ]] 用于比较字符串、判断文件等,功能比较复杂多样,这里先使用最基础的用法。注意尽量不要用 [[ ]] 比较数值,因为不留神的话,数值会被转化成字符串来比较,没有任何错误提示,但结果可能不符合预期,导致不必要的麻烦。

1
2
3
4
# 样例
if [[ "$str" == "name" || "$str" == "value" ]] {
echo "$str"
}

(( )) 用于比较数值,里边可以调用各种数值相关的函数,格式类似 c 语言,变量前的 $ 可省略。

1
2
3
# 格式
if (( )) {
}
1
2
3
4
# 样例
if ((num > 3 && num + 3 < 10)) {
echo $num
}

{ } 用于在当前 sh 运行命令并且判断运行结果。

1
2
3
# 格式
if { } {
}
1
2
3
4
# 样例
if {grep sd1 /etc/fstab} {
echo good
}

( ) 用于在子 sh 运行命令并且判断运行结果,用法和 {} 类似,不再举例。

1
2
3
# 格式
if ( ) {
}

这几种括号可以一起使用,这样可以同时判断字符串、数值、文件、命令结果等等。最好不要混合使用 && ||,会导致可读性变差和容易出错。

1
2
3
# 格式
if [[ ]] && (( )) && { } {
}

循环语句

1
2
3
4
# 格式
while [[ ]] {
break/continue
}

if 一样,这里的 [[ ]] 可以替换成其他几种括号,功能也是一样的,不再依次举例。break 用于结束循环,continue用于直接进入下一次循环。所有的循环语句中都可以使用 breakcontinue,下边不再赘述。

1
2
3
4
# 样例 死循环
while ((1)) {
echo good
}

untilwhile 相反,不满足条件时运行,一旦满足则停止,其他的用法和 while 相同,不再举例。

1
2
3
# 格式
until [[ ]] {
}

for 循环主要用于枚举,这里的括号是 for 的特有用法,不是在子 sh 执行。括号内是字符串(可放多个,空格隔开)、数组(可放多个)或者哈希表(可放多个,哈希表是枚举值而不是键)。i 是用于枚举内容的变量名,变量名随意。

1
2
3
# 格式
for i ( ) {
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 样例
for i (aa bb cc) {
echo $i
}
# 枚举当前目录的 txt 文件
for i (*.txt) {
echo $i
}
# 枚举数组
array=(aa bb cc)
for i ($array) {
echo $i
}

经典的 c 风格 for 循环。

1
2
3
# 格式
for (( ; ; )) {
}
1
2
3
4
# 样例
for ((i=0; i < 10; i++)) {
echo $i
}

这个样例只是举例,实际上多数情况不需要使用这种 for 循环,可以这样。

1
2
3
4
# 样例,{1..10} 可以生成一个 1 到 10 的数组
for i ({1..10}) {
echo $i
}

repeat 语句用于循环固定次数,n 是一个整数或者内容为整数的变量。

1
2
3
# 格式
repeat n {
}
1
2
3
4
# 样例
repeat 5 {
echo good
}

分支语句

分支逻辑用 if 也可以实现,但 case 更适合这种场景,并且功能更强大。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 格式 + 样例
case $i {
(a)
echo 1
;;
(b)
echo 2
# 继续执行下一个
;&
(c)
echo 3
# 继续向下匹配
;|
(c)
echo 33
;;
(d)
echo 4
;;
(*)
echo other
;;
}

;; 代表结束 case 语句,;& 代表继续执行紧接着的下一个匹配的语句(不再进行匹配),;| 代表继续往下匹配看是否有满足条件的分支。

用户输入选择语句

select 语句是用于根据用户的选择决定分支的语句,语法和 for 语句差不多,如果不 break,会循环让用户选择。

1
2
3
# 格式
select i ( ) {
}
1
2
3
4
# 样例
select i (aa bb cc) {
echo $i
}

输出是这样的。

1
2
1) aa 2) bb 3) cc
?#

按上边的数字加回车来选择。

异常处理语句

1
2
3
4
5
6
# 格式
{
语句 1
} always {
语句 2
}

如果语句 1 执行出错,则执行语句 2。

简化的条件语句

if 语句的简化版,在只有一个分支的情况下更简洁,功能和 if 语句类似,不赘述

1
2
3
4
5
6
格式:
[[ ]] || {
}
[[ ]] && {
}

最好不要连续混合使用 && ||,比如。

1
aa && bb || cc && dd

容易导致逻辑错误或者误解,可以用 { } 把语句包含起来。

1
aa && { bb || { cc && dd } }

比较复杂的判断还是用 if 可读写更好,&& || 通常只适用于简单的场景。

参考

zsh-开发指南