最近工作中需要用到 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
zsh 数组截取
echo "$str[2,4]" # 这里会输出「bcd」,2 和 4 都是字符在数组的位置,从 1 开始数,逗号两边不能有空格 echo "$str[4,-1]" # 这里输出「def」,-1 是最后一个字符
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 个字符开始,截取到结束
# 号截取 —— 删除左边字符,保留右边字符 (可用于 bash)
var=http://crazybunqnq.com/react-tetris echo ${var#*//} # 这里输出「crazybunqnq.com/react-tetris」 # 其中「var」是变量名,「#」号是运算符,「*//」表示从左边开始删除第一个「//」字符串及左边的所有字符 echo ${var##*/} # 这里输出「react-tetris」 # 其中「##」是运算符,「/*」表示从右边开始删除第一个(即左数最后一个)「/」字符串及左边的所有字符
% 号截取 —— 删除右边字符,保留左边字符(可用于 bash)
与 # 号截取类似
var=http://crazybunqnq.com/react-tetris echo ${var%/r*} # 这里输出「http://crazybunqnq.com」 # 其中「%」是运算符,「/r*」表示从左边开始删除第一个「/r」字符串及右边的所有字符 echo ${var%%/*} # 这里输出「http://crazybunqnq.com」 # 其中「%%」是运算符,「/*」表示从右边开始删除第一个 (即左数最后一个)「/」字符串及游遍所有字符
# 表示注释,我好像在说废话…一看颜色就知道了,哈哈哈哈哈
### 变量比较
比较数值
(( ))
用于数值比较等操作,如果为真返回 0,否则返回 1&&
后边的语句在前面的语句为真时才会执行必须使用双等号
==
来比较num=123 ((num == 123)) && echo good # 这里会输出「good」
((
里面可以使用与&&
或||
非!
操作符((num == 1 || num == 2)) && echo good # 这里会输出「good」
比较字符串
比较字符串要用
[[ ]]
,内侧要有空格这里的双等号
==
可以替换成单等号=
$str 两侧不需要加双引号,即使 str 未定义或者 $str 中含有空格和特殊符号
str=abc [[ $str == abc ]] && good # 这里会输出「good」
可以和空字符串
""
比较,未定义的字符串和空字符串比较结果为真[[
里也可以使用&&
、||
和!
[[ $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
用于直接进入下一次循环。所有的循环语句中都可以使用break
和continue
。
死循环:
while ((1)) {
echo good
}
#### until 循环
until
和 while
相反,不满足条件时运行,一旦满足则停止运行,其他的用法和 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
嗯…暂时用到过的大概就这些,在遇到的之后再补充吧~
感谢阅读,有问题可以在下面留言哟~