注:本学习笔记来源于视频:尚硅谷-3天搞定Linux,1天搞定Shell(2022版)。光看这一个视频对于 Linux 和 Shell 了解还是有些不够,可以结合视频 黑马-2019全新Shell脚本从入门到精通教程,该视频介绍了更多命令的使用。两个视频结合起来学习,能加深对 Shell 脚本的掌握。
第 1章 Shell 概述
Shell 是一个命令行解释器,它接收应用程序或用户命令(如 ls
等命令),然后调用操作系统内核。同时 Shell 也是一个功能强大的脚本语言。
- 可以通过
cat /etc/shells
查看当前系统提供的 Shell 解析器:
bash
和sh
的关系:实际上sh
就是bash
的软链接。
- 当前系统默认的 Shell 解析器可以通过
echo $SHELL
命令查看:
第 2 章 Shell 脚本入门
2.1 脚本格式
脚本以 #!/bin/bash
开头,指定解析器。
2.2 第一个 Shell 脚本:hello.sh
使用 vim hello.sh
创建第一个脚本,然后输入如下内容:
#!/bin/bash
echo "hello world!"
2.3 脚本的常用执行方式
2.3.1 第一种执行方式
采用 bash 脚本路径
或 sh 脚本路径
的方式执行脚本。其中 脚本路径
可以是脚本的相对路径或绝对路径。该种方式不需要赋予脚本执行权限。
# 语法
sh 脚本相对路径
sh 脚本绝对路径
bash 脚本相对路径
bash 脚本绝对路径
# 示例
sh ./hello.sh
sh /root/hello.sh
bash ./hello.sh
bash /root/hello.sh
2.3.2 第二种执行方式
可以采用在命令行种输入脚本的绝对路径或相对路径执行脚本,但前提是必须具有可执行权限 +x
。
所以首先要为脚本文件(如 hello.sh
)添加可执行权限:
# 语法
chmod +x 脚本文件名
# 示例
chmod +x hello.sh
接着输入脚本的绝对路径或相对路径来执行脚本:
# 语法
脚本的相对路径
脚本的绝对路径
# 示例
./hello.sh
/root/hello.sh
注意:第一种执行方法,本质是 bash 解析器帮你执行脚本,所以脚本本身不需要执行权限。第二种执行方法,本质是脚本需要自己执行,所以需要执行权限。
第三种执行方式
在脚本的路径前加上 .
或者 source
来执行脚本:
# 语法
. 脚本路径
# 或
source 脚本路径
前两种方式都是在当前 shell 中打开一个子 shell 来执行脚本内容,当脚本内容结束,则子 shell 关闭,回到父 shell 中。第三种,也就是使用在脚本路径前加 .
或者 source
的方式,可以使脚本内容在当前 shell 里执行,而无需打开子 shell。这也是为什么我们每次要修改完 /etc/profile
文件以后,需要 source 一下的原因。
开子 shell 与不开子 shell 的区别就在于,环境变量的继承关系,如在子 shell 中设置的当前变量,父 shell 是不可见的。
在当前命令行窗口输入 bash
即可进入一个子 shell,使用 exit
退出子 shell 回到父 shell。
第 3 章 变量
3.1 系统预定义变量
常用的系统变量有:$HOME
、$PWD
、$SHELL
、$USER
等。可以通过 echo $系统变量名
进行查看:
如果要查看当前 Shell 的所有变量(包括普通变量和系统变量),可以通过 set
命令查看:
3.2 自定义变量
3.2.1 声明变量
自定义变量的语法如下:
# 语法,注意等号两侧不能有空格
变量名=变量值
# 示例
path="/root/"
count=3
还有一种静态变量,即不能 unset
的变量。语法如下:
# 语法,声明静态变量
readonly 静态变量名=变量值
# 示例
readonly a=123
3.2.2 撤销变量
因为已经创建的变量不能随便删除,只能撤销,所以撤销变量的语法如下:
# 语法
unset 变量名
# 示例
unset path
unset count
3.2.3 调用变量
如果要获取变量的值,语法如下:
# 语法,即在变量名的前面加上美元符号 $ 即可访问到该变量的值
echo $变量名
# 示例
echo $USER
echo $info
3.2.4 变量定义规则
变量定义需要遵循一定的规则,如下:
- (1)变量名称可以由字母、数字和下划线组成,但是不能以数字开头,环境变量名建议大写。
- (2)等号两侧不能有空格
- (3)在 bash 中,变量默认类型都是字符串类型,无法直接进行数值运算。
- (4)变量的值如果有空格,需要使用双引号或单引号括起来。建议如果是文件路径则用双引号括起来。
3.2.5 变量实例
# 定义变量 A 并进行访问
A=123
echo $A
# 给变量 A 重新赋值
A=456
echo $A
# 撤销变量 A
unset A
echo $A
# 声明静态变量 B=3,不能 unset
readonly B=3
echo $B
B=9 # 不能
unset B # 不能
# 在 bash 中,变量默认类型都是字符串类型,无法直接进行数值运算
C=1+2
echo $C # 结果为 1+2
# 变量的值如果有空格,需要用双引号或单引号括起来
D="hello world"
echo $D
# 可以将变量提升为全局环境变量,可以供其他 shell 程序使用。即在任意脚本文件中都可以直接使用这个全局变量
export 变量名
3.3 特殊变量
3.3.1 $n
基本语法:
$n
作用:n
为数字,$0
代表该脚本名称,$1
-$9
代表第一到第九个参数,十以上的参数,十以上的参数需要用大括号包含,如${10}
。
示例:test.sh
#!/bin/bash
echo "==================$n=================="
# 获取脚本名
echo $0
# 获取传入的第一个参数
echo $1
# 获取传入的第二个参数
echo $2
3.3.2 $#
基本语法:
$#
作用:获取所有输入参数个数,常用于循环,判断参数的个数是否正确以及加强脚本的健壮性。
示例:
#!/bin/bash
echo '==================$n=================='
# 获取脚本名
echo $0
# 获取传入的第一个参数
echo $1
# 获取传入的第二个参数
echo $2
echo '==================$#=================='
# 获取传入的参数个数
echo $#
3.3.3 $*
与 $@
语法如下:
# 这个变量代表命令行中所有的参数,$* 把所有的参数看成一个整体
$*
# 这个变量也代表命令行中所有的参数,不过 $@ 把每个参数区分对待
$@
示例:
#!/bin/bash
echo '==================$n=================='
# 获取脚本名
echo $0
# 获取传入的第一个参数
echo $1
# 获取传入的第二个参数
echo $2
echo '==================$#=================='
# 获取传入的参数个数
echo $#
echo '==================$*=================='
for param1 in "$*"; do
echo $param1
done
echo '==================$@=================='
for param2 in "$@"; do
echo $param2
done
$?
语法如下:
$?
作用:最后一次执行的命令的返回状态。如果这个变量的值为 0,证明上一个命令正确执行;如果这个变量的值为非 0(具体是哪个数,由命令自己来决定),则证明上一个命令执行不正确了。
示例:
# 例如,输入date命令,然后查看执行返回的状态
date
echo $?
第 4 章 运算符
所谓的运算符指的是:()
、+
、-
、*
、/
、%
等运算符。
在 Linux 系统中如果要进行数学运算,是不能直接运算的,需要使用命令或者遵循如下格式:
# 语法
$((运算式))
#或
$[运算式]
# 示例
result=$(((2+3)*5))
echo $result
result=$[(2+3)*5]
echo $result
第 5 章 条件判断
5.1 基本语法
在 Shell 中进行条件判断,有如下语法(通常使用第二种方式):
# 语法
# 第一种方式
test 条件表达式
# 第二种方式,注意条件表达式前后必须要有空格。条件表达式非空即为 true。如 [ hello ] 返回 true,[ ] 返回 false。
[ 条件表达式 ]
5.2 常用判断条件
5.2.1 比较两个整数
如果要比较两个整数,是不能使用 >
、=
等符号的,需要使用如下判断:
符号 | 说明 |
---|---|
-eq | 等于(equal) |
-ne | 不等于(not equal) |
-lt | 小于(less than) |
-le | 小于等于(less equal) |
-gt | 大于(greater than) |
-ge | 大于等于(greater than) |
例如,比较 23 是否大于等于20:
5.2.2 按照文件权限判断
如果要判断文件,则判断如下:
符号 | 说明 |
---|---|
-e 文件路径 | 如果文件存在则为真 |
-r 文件路径 | 如果文件存在且可读则为真 |
-w 文件路径 | 如果文件存在且可写则为真 |
-x 文件路径 | 如果文件存在且可执行则为真 |
-s 文件路径 | 如果文件存在且至少有一个字符则为真 |
-d 文件路径 | 如果文件存在且为目录则为真 |
-f 文件路径 | 如果文件存在且为普通文件则为真 |
-c 文件路径 | 如果文件存在且为字符设备则为真 |
-b 文件路径 | 如果文件存在且为块设备则为真 |
例如,判断 test.txt
文件是否是普通文件:
5.2.3 字符串比较
在 Shell 脚本中字符串的比较不能用 -eq
,而是需要使用如下的格式:
符号 | 说明 |
---|---|
str1 = str2 | 两个字符串相等则为真 |
str1 != str2 | 两个字符串不相等则为真 |
-z str | 如果字符串 str 的长度为零则为零 |
-n str | 如果字符串 str 的长度不为零则为真 |
例如,判断字符串 "abc"
与 "abc"
是否相等:
5.3 多条件判断
如果要使用多条件判断,类似于三元表达式,语法如下:
# 语法
[ 条件表达式 ] && 条件成立执行 || 条件不成立执行
# 示例
[ hello ] && echo OK || echo notOK #OK
[ ] && echo OK || echo notOK #notOK
注:
&&
表示前一条命令执行成功时,才执行后一条命令。||
表示上一条命令执行失败后,才执行下一条命令。
除了上述的类似于三元表达式的语法之外,还提供了“与”、“或”、“非”的语法:
# 与(有两种语法),表示多个条件都满足时才成立
[ 条件表达式1 ] && [ 条件表达式2 ]
[ 条件表达式1 -a 条件表达式2 ]
# 或(也有两种语法),表示只要有一个条件满足就成立
[ 条件表达式1 ] || [ 条件表达式2 ]
[ 条件表达式1 -o 条件表达式2 ]
# 非,表示取反
! 条件表达式
第 6 章 流程控制
6.1 if
判断
- 单分支
if [ 条件判断式 ]; then
条件体
fi
# 或
if [ 条件判断式 ]
then
条件体
fi
- 多分支
if [ 条件判断式 ]; then
条件体
elif [ 条件判断式 ]; then
条件体
else
条件体
fi
示例如下:
#!/bin/bash
a=2
b=3
if [ $a -gt $b ]; then
echo "a>b"
elif [ $a -lt $b ]; then
echo "a<b"
else
echo "a=b"
fi
注:
[ 条件判断式 ]
中括号和条件判断式之间必须要有空格。
if
后面要隔着空格。
;
表示一行上执行多条命令。
6.2 case
语句
基本语法如下:
case $变量名 in
"值 1")
如果变量的值等于值 1,则执行程序 1
;;
"值 2")
如果变量的值等于值 2,则执行程序 2
;;
…省略其他分支…
*)
如果变量的值都不是以上的值,则执行此程序
;;
esac
例如:
#!/bin/bash
name="root"
case $name in
"zhangsan")
echo "zhangsan"
;;
"root")
echo "root"
;;
*)
echo "other user"
;;
esac
注:
case
行尾必须为单词in
,每个模式匹配必须以右括号)
结束。- 待匹配的内容可以是字符、字符串、数字等。
- 双分号
;;
表示命令序列结束,相当于 Java 中的break
。- 最后的
*)
表示默认,相当于 Java 中的default
。
6.3 for
循环
基本语法如下:
for (( 循环变量;循环控制条件;循环变量变化 ))
do
循环体
done
例如:从 1 加 到 100 的总和
#!/bin/bash
sum=0
for (( i=0;i<=100;i++ ))
do
sum=$(($sum+$i))
done
echo "1+2+...+100=$sum"
除了上面的 for
循环之外,还有一类 for...in
循环,通常用来循环读取文件所有行。基本语法如下:
for 变量 in 值1 值2 值3 ...
do
循环体
done
示例如下:
#!/bin/bash
for i in zhangsan lisi root wangwu
do
echo "the user is $i"
done
注:
$*
和$@
都表示传递给函数或者脚本的所有参数,但是当它们不被双引号包含时(即$*
和$@
),都以$1
、$2
、……、$n
的形式输出所有参数;当它们被双引号包含时(即"$*"
和"$@"
),其中"$*"
会将所有的参数当作一个整体,以"$1 $2 ... $n"
的形式输出所有参数,而"$@"
会将各个参数都分开,以"$1"
、"$2"
、……、"$n"
的形式输出所有参数。
6.4 while
循环
基本语法如下:
while [ 条件判断式 ]
do
循环体
done
例如:从 1 加 到 100 的总和
#!/bin/bash
i=1
sum=0
while [ $i -le 100 ]
do
sum=$(($sum+$i))
i=$(($i+1))
done
echo "1+2+...+100=$sum"
第 7 章 读取控制台输入
其实就是读取用户输入的信息,需要用到 read
命令。该命令的使用可参考:Linux命令之获取用户键盘输入read。
第 8 章 函数
8.1 系统函数
主要将了两个命令的使用:
8.2 自定义函数
基本语法如下:
# 定义函数
[function] 函数名[()]
{
函数体;
[return 整数;]
}
# 调用函数
函数名 参数1 参数2 ...
示例如下:
#!/bin/bash
# 定义函数:计算两数之和
function sum()
{
result=0
result=$(($1+$2))
echo "$result"
}
# 调用函数
read -p "请输入第一个整数:" n1
read -p "请输入第二个整数:" n2
sum $n1 $n2
注意事项:
- 必须在调用函数地方之前,先声明函数,shell 脚本是逐行运行。不会像其它语言一
样先编译。 - 函数返回值,只能通过
$?
c系统变量获得,可以显示加return 整数
返回,如果不加,将
以最后一条命令运行结果,作为返回值。return
后跟数值范围只能是0~255
。 - 如果要获取传递给函数的参数,可以通过
$1
、$2
这样的语法,即跟获取传递给脚本的参数一样的语法来获得。 - 如果要获取脚本的绝对路径,可以使用这样的命令:
$(cd $(dirname $0); pwd)
。 - 如果想要获取返回的是非数值或大于 255 的整数,那么可以使用
echo
命令输出返回结果,然后在函数外用命令替换的方式$(函数名 参数1 参数2 ...)
获取到函数的执行结果。
第 9 章 正则表达式入门
正则表达式使用单个字符串来描述、匹配一系列符合某个语法规则的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些符合某个模式的文本。在 Linux 中,grep
、sed
、awk
等文本处理工具都支持通过正则表达式进行模式匹配。
注:如果已经学会了正则表达式那么直接学
grep
、sed
、awk
命令中关于正则表达式中的应用即可。如果没有学会正则表达式那么可以先了解基础的正则表达式。关于正则表达式的学习可参考:正则表达式学习笔记,不必先完全学会正则表达,学会一些常用的用法就可以灵活使用在 Linux 命令上了。
第 10 章 文本处理工具
本章讲了常用命令:
- Linux命令之提取行指定范围内容cut
awk
命令
注:除此之外,还有
sed
命令,也是常用的文本处理工具。