zsh 基础


最近工作中需要用到 shell 脚本,正好学习 zsh 在我的学习清单里,反正早晚都要学,正好现在用到了,就边学边做了。

我将会在这篇博客中整理记录我目前使用到的 shell 脚本知识,应该都是基础知识,哈哈。

Linux 自带的 shell 是 bash,我这学习的是 zsh(bash 能做的它都能做~),但是基础部分应该都是差不多的。
网上关于 zsh 的文章大部分都是介绍如何安装和配置 zsh,所以这里就不介绍 zsh 的安装、使用和配置了。
另外至于为什么使用 zsh 也不做过多解释,因为喜欢:)

变量

接触一门新的编程语言,运行完 Hello World 后,首先要了解的基本就是如何定义和使用变量了。有了变量后可以比较变量内容,进而可以解除条件、循环、分支等语句,继而了解函数的用法…

所以,先来介绍变量和语句。

变量定义

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

zsh 有 5 中变量:整数、浮点数(bash 不支持)、字符串、数组、哈希表(或者叫关联数组或字典),另外还有一些其他语言少有的东西,比如 alias(主要是交互的时候使用,编程基本用不到)。

整数与浮点数

num1=123
num2=123.456

等号两端不能有空格

字符串

str1=abcde
str2='abc def'

如果字符串中包含空格等特殊字符,需要加引号

str3="abc def $num1"

也可以用双引号,但和单引号有区别,比如双引号里可以使用变量
使用 $变量 来使用之前定义的 变量

str4="abc\tdef\ng"

在字符串中可以使用转义字符,单双引号均可

输入参数

输入参数是用户在使用 shell 脚本的时候输入的变量,例如 test.sh 中的代码如下:

in1=$1
in2=$2
in3=$3

echo "用户输入的内容是:$1 $2 $3"

执行 test.sh 脚本时在终端中输入下面这行代码:

sh test.sh 欢迎阅读 CrazyBunQnQ 的博客

该代码将会输出

echo "用户输入的内容是:欢迎阅读 CrazyBunQnQ 的博客"
  • sh 是调用 shell 脚本的命令
  • test.sh 是需要执行的 shell 脚本文件名,shell 文件类似 Windows 中的 .bat 批处理文件
  • 再之后的 test.sh 对应的参数,每个参数之间用空格隔开

### 变量操作

输出变量

echo $str1

也可以使用 print,上面这行会输出变量 str1 的值 abcde
打桩法调试的时候很常用

echo $str3 > log.log
echo $str4 >> log.log

echo A > B 会将 A 的内容写入 B 文件中
echo A >> B 也会将 A 的内容写入到 B 文件中

需要注意的是 >>> 不同:
> 是重新写入文件
>> 是在文件中追加内容

如果 B 不存在,则会创建 B 文件
常用于输出日志

简单的数值计算

num3=$(($num1 + $num2))
num3=$((num1 + num2))

(( 中的变量可以不使用 $

截取字符串的几种方法

定义字符串如下:

str=abcdef
  1. zsh 数组截取

    echo "$str[2,4]"
    # 这里会输出「bcd」,2 和 4 都是字符在数组的位置,从 1 开始数,逗号两边不能有空格
    echo "$str[4,-1]"
    # 这里输出「def」,-1 是最后一个字符
  2. bash 数组截取

    echo "${str:0:3}"
    # 这里输出「abc」,从左边第 1 个字符开始,截取 3 个字符
    # 注意这里左边第一个字符从 0 开始,上面是从 1 开始
    echo "${str:3}"
    # 这里输出「def」,从左边第 4 个字符开始,一直到结束,也是从 0 开始计数
    echo "${str:0-4:3}"
    # 这里输出「cde」,从右数第 4 个字符开始,截取 3 个字符,不能使用「-4」,必须要有 0
    echo "${str:0-3}"
    # 这里输出「def」,从右数第 3 个字符开始,截取到结束
  3. # 号截取 —— 删除左边字符,保留右边字符 (可用于 bash)

    var=http://crazybunqnq.com/react-tetris
    echo ${var#*//}
    # 这里输出「crazybunqnq.com/react-tetris」
    # 其中「var」是变量名,「#」号是运算符,「*//」表示从左边开始删除第一个「//」字符串及左边的所有字符
    
    echo ${var##*/}
    # 这里输出「react-tetris」
    # 其中「##」是运算符,「/*」表示从右边开始删除第一个(即左数最后一个)「/」字符串及左边的所有字符
  4. % 号截取 —— 删除右边字符,保留左边字符(可用于 bash)

    与 # 号截取类似

    var=http://crazybunqnq.com/react-tetris
    echo ${var%/r*}
    # 这里输出「http://crazybunqnq.com」
    # 其中「%」是运算符,「/r*」表示从左边开始删除第一个「/r」字符串及右边的所有字符
    
    echo ${var%%/*}
    # 这里输出「http://crazybunqnq.com」
    # 其中「%%」是运算符,「/*」表示从右边开始删除第一个 (即左数最后一个)「/」字符串及游遍所有字符

# 表示注释,我好像在说废话…一看颜色就知道了,哈哈哈哈哈


### 变量比较

比较数值

  1. (( )) 用于数值比较等操作,如果为真返回 0,否则返回 1

  2. && 后边的语句在前面的语句为真时才会执行

  3. 必须使用双等号 == 来比较

    num=123
    ((num == 123)) && echo good
    # 这里会输出「good」
  4. (( 里面可以使用与 &&||! 操作符

    ((num == 1 || num == 2)) && echo good
    # 这里会输出「good」

比较字符串

  1. 比较字符串要用 [[ ]]内侧要有空格

  2. 这里的双等号 == 可以替换成单等号 =

  3. $str 两侧不需要加双引号,即使 str 未定义或者 $str 中含有空格和特殊符号

    str=abc
    [[ $str == abc ]] && good
    # 这里会输出「good」
  4. 可以和空字符串 "" 比较,未定义的字符串和空字符串比较结果为真

  5. [[ 里也可以使用 &&||!

    [[ $str == "" || $str == 123 ]] && echo good
    # 这里会输出「good」

语句

简单的变量使用介绍完了,来说说语句部分。

条件语句

[[ ]] 用于比较字符串、判断文件等

格式:

if [[ ]] {
} elif {
} else {
}

注意 elif 不可写作 else if
尽量不要使用 [[ ]] 比较数值,因为不留神的话,数值会被转化成字符串来比较,没有任何错误提示

例子:

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

(( )) 用于比较数值,里面可以调用各种数值相关的函数,变量前的 $ 可省略。

格式:

if (( )) {
}

例子:

if ((num > 3 && num + 3 < 10)) {
    echo $num
}

{ } 用于在当前 sehll 里运行 { } 中的命令并判断运行结果

格式:

if { } {
}

例子:

if {echo '执行命令'} {
    echo '执行成功'
}

( ) 用于在子 shell 里运行 ( ) 中的命令并判断运行结果

格式:

if ( ) {
}

用法和 { } 类似。

混合条件

以上几种括号可以一起使用,这样可以同时判断字符串、数值、文件、命令结果等。

但是建议不要混合使用 &&||,会导致可读性变差且容易出错。

格式:

if [[ ]] && (( )) && { } {
}

### 循环语句

刚刚已经介绍完条件语句了,所以我就默认大家都大概了解各种括号的使用方法了,循环语句中的括号用法和 if 一样,示例中的 [[ ]] 可以替换成其他几种括号,功能也是一样的,不再依次举例了,大家可以自己试试。

while 循环

格式:

while [[ ]] {
    break/continue
}

这里的 break 用于结束循环,continue 用于直接进入下一次循环。所有的循环语句中都可以使用 breakcontinue

死循环:

while ((1)) {
    echo good
}

#### until 循环

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

格式:

until [[ ]] {
}

#### for 循环

格式 1:

for i ( ) {
}

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

例 1:

for i (aa bb cc) {
    echo $i
}
# 执行后会输出如下内容
# aa
# bb
# cc

例 2:枚举当前目录的 txt 文件

for i (*.txt) {
    echo $i
}

例 3:

arr=(aa bb cc)
for i ($arr) {
    echo $i
}
# 执行后会输出如下内容
# aa
# bb
# cc

格式 2:

for (( ; ; )) {
}

这个感觉很熟悉啊,哈哈哈哈

例子:

for ((i=0; i<10; i++)) {
    echo $i
}
# 执行后会依次输出 0~9

但是实际上多数情况不需要使用这种 for 循环,可以使用下面的方法。

格式 3:

for i ({1..10}) {
    echo $i
}
# 执行后会依次输出 1~10

{1..10} 可以生成一个 1 到 10 的数组


#### repeat 循环

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

格式:

repeat n {
}

例子:

repeat 5 {
    echo good
}
# 执行后会输出五行「good」

### 分支语句

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

格式 + 例子:

i=a
#i=b
#i=c
#i=d
#i=e
case $i {
    (a)
    echo 1
    ;;

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

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

    (c)
    echo 33
    ;;

    (d)
    echo 4
    ;;

    (*)
    echo other
    ;;
}

其中:

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

大家可以在上面的例子前尝试给 i 赋值不同的字符,看看效果,感觉比 java 简洁了好多啊!!!


#### 用户输入选择语句

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

格式:

select i () {
}

例子:

select i (aa bb cc) {
    echo $i
}

执行完上面的例子后是这样的:

用户选择语句


#### 异常处理语句

格式:

{
    语句 1
} always {
    语句 2
}

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


#### 简化的条件语句

格式:

[[ ]] || {
}

[[ ]] && {
}

在只有一个分支的情况下,可以使用简化的 if 语句,功能和 if 语句类似。就不举例啦!

再次强调,强烈建议不要连续混合使用 &&||,例如:

aa && bb || cc && dd

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

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

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


## 一些常用命令

先说下命令如何使用吧

  • 符号 - 后面是命令选项,多个可以连写,只需要一个 - 即可。
    例如下面表格中的 ls -al 命令,-a 表示显示隐藏文件,-l 表示格式化目录列表,同时使用则为 -al
  • 命令后面用空格隔开的、没有 - 号的内容为命令的参数
  • 多个参数之间用空格隔开,并且参数有顺序

|命令|作用|
|:-|:-:|:-|:-:|
|ls|查看当前目录内容|
|pwd|显示当前目录|
|ls -a|查看当前目录所有内容|
|ls -al|显示并格式化当前目录所有内容|
|rm file|删除文件|
|rm -r dir|删除目录|
|rm -f|强制删除文件|
|rm -rf dir|强制删除目录|
|mkdir dir|创建目录|
|cp file1 file2|复制 file1 到 file2|
|mv file1 file2|移动或重命名 file1 为 file2|
|cd dir|改变当前目录为 dir|
|cd ..|返回上一级目录|
|tr -d str <file1> file2|删除 file1 文件中所有的 str 并保存为 file2 文件|


### chmod 权限命令

这个命令略微复杂,详细说一下吧

格式:

chmod 八进制数 需要授权的文件

其中,八进制数代表以下三个权限对应的数值之和:

  • 4 - 读权限(r)
  • 2 - 写权限(w)
  • 1 - 执行权限(x)

并且,这个八进制数有三位,按顺序分别对应三种用户的权限:

拥有者/group/world

例如:

# 为 test.txt 文件授予所有用户 rwx 权限(读、写和执行)
chmod 777 test.txt
# 为 test.txt 文件授予拥有者 rw 权限,授予 group 用户 rx 权限,授予 world 用户 w 权限
chmod 754 test.txt

嗯…暂时用到过的大概就这些,在遇到的之后再补充吧~
感谢阅读,有问题可以在下面留言哟~


文章作者: CrazyBunQnQ
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC 4.0 许可协议。转载请注明来源 CrazyBunQnQ !
 上一篇
VS Code 正则表达式查找替换 VS Code 正则表达式查找替换
最近做的事情经常要处理重复的文本,用了一段时间之后,利用正则表达式来查找字符串基本上是很熟练啦~但是前几天看到 drakeet 大神使用正则表达式把自己项目中所有下划线命名的变量改为驼峰命名法之后我知道,原来正则表达式还可以这么玩!哇塞,之
2017-12-18
下一篇 
Python 3 的函数 Python 3 的函数
字符串是什么? 其实经过上一篇博客的阅读与训练,你已经掌握了函数的用法: print 是一个放入对象就能将结果打印的函数 input 是一个可以让用户输入信息的函数 len 是一个可以测量对象长度的函数 int 是一个可以将字符串类
2017-11-18
  目录