zsh-变量和语句

格式约定

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

变量

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

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

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

变量定义

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

# 等号两端不能有空格
% 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

变量比较

# 比较数值
% 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 也是没有问题的。

条件语句

# 格式
if [[ ]] {
} elif {
} else {
}

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

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

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

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

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

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

# 格式
if { } {
}
# 样例
if {grep sd1 /etc/fstab} {
    echo good
}

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

# 格式
if ( ) {
}

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

# 格式
if [[ ]] && (( )) && { } {
}

循环语句

# 格式
while [[ ]] {
    break/continue
}

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

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

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

# 格式
until [[ ]] {
}

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

# 格式
for i ( ) {
}
# 样例
for i (aa bb cc) {
    echo $i
}

# 枚举当前目录的 txt 文件
for i (*.txt) { 
    echo $i
}

# 枚举数组
array=(aa bb cc)
for i ($array) {
    echo $i
}

经典的 c 风格 for 循环。

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

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

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

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

# 格式
repeat n {
}
# 样例
repeat 5 {
    echo good
}

分支语句

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

# 格式 + 样例
case $i {
    (a)
    echo 1
    ;;

    (b)
    echo 2
    # 继续执行下一个
    ;&

    (c)
    echo 3
    # 继续向下匹配
    ;|

    (c)
    echo 33
    ;;

    (d)
    echo 4
    ;;

    (*)
    echo other
    ;;
}

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

用户输入选择语句

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

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

输出是这样的。

1) aa  2) bb  3) cc
?#

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

异常处理语句

# 格式
{
    语句 1
} always {
    语句 2
}

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

简化的条件语句

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

格式:
[[ ]] || {
}

[[ ]] && {
}

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

aa && bb || cc && dd

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

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

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

参考

zsh-开发指南

results matching ""

    No results matching ""