09. Shell脚本
一、什么是Shell
Shell 是操作系统中的一个 命令行解释器,主要功能是接收用户命令,然后将这些命令传递给操作系统内核去执行。 Shell 是用户与操作系统内核之间的接口,它允许用户通过命令行或脚本来与操作系统进行交互。同时,Shell 也是一种脚本语言,允许用户编写一系列命令脚本(Shell脚本)以实现自动化任务处理。
我们可以查看 /etc/shells 文件查看 Linux 提供的 Shell 解释器有哪些。
cat /etc/shells
然后,我们可以执行 echo $SHELL 命令查看默认使用的是哪个 Shell 解释器。
echo $SHELL

二、一个简易的Shell脚本
这里,我们使用 vim 新建一个 Shell 脚本。然后,我们在 hello.sh 脚本文件中编写脚本。
#!/bin/bash
echo "Hello world!"
编写完成之后,我们保存退出该脚本文件。然后,我们可以在终端中通过 sh 命令执行 Shell 脚本。
sh hello.sh
我们也可以直接运行 hello.sh 脚本文件。
./hello.sh
当我们直接运行脚本文件时会发现显示权限,这是因为,hello.sh 脚本文件默认没有执行权限。此时,我们可以通过 chmod 命令修改文件权限。

三、变量
在 Shell 编程中,变量是用于存储数据值的名称。我们可以使用 变量名=变量值 的方式 定义变量,然后通过 $变量名 的方式 获取变量的值。在定义变量时,变量名和等号之间不能有空格。如果变量后面不想使用的话,可以使用 unset 变量名 的方式撤销变量。如果我们要 定义只读变量,可以使用 readonly 变量名=变量值 的方式。只读变量不能使用 unset 撤销。如果我们想要变量 提升为全局变量 供给其它 Shell 程序使用,可以使用 export 变量名 的方式。
同时,变量名的命名须遵循如下规则:
- 只包含字母、数字和下划线:变量名可以包含字母(大小写敏感)、数字和下划线,不能包含其他特殊字符。
- 不能以数字开头:变量名不能以数字开头,但可以包含数字。
- 避免使用 Shell 关键字:不要使用Shell的关键字(例如 if、then、else、fi、for、while 等)作为变量名,以免引起混淆。
- 避免使用空格:变量名中不应该包含空格,因为空格通常用于分隔命令和参数。
在 Shell 中,变量值默认都是字符串,如果字符串直接包含空格,则要使用单引号或者双引号括起来。

我们在使用 Shell 时,Shell 中有几个特殊字符用来处理参数:。
$n:在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为$n,n 代表一个数字,0 是 执行的文件名,1 为执行脚本的第一个参数,是传入的第一个参数。2 为执行脚本的第二个参数。如果 n 大于 9,需要使用大括号括起来。$#:传递到脚本的参数个数。$*:以一个单字符串显示所有向脚本传递的参数。$@:与$*相同,但是使用时加引号,并在引号中返回每个参数。$?:显示最后命令的退出状态。0 表示没有错误,其它任何值表明有错误。$$:脚本运行的当前进程 ID 号。$-:显示 Shell 使用的当前选项,与 set 命令功能相同。
#!/bin/bash
echo '---------- $n ----------'
echo $0
echo $1
echo $2
echo ${10}
echo '---------- $# ----------'
echo $#
echo '---------- $* ----------'
echo $*
echo '---------- $@ ----------'
echo $@
echo '---------- $? ----------'
echo $?
echo '---------- $$ ----------'
echo $$
echo '---------- $- ----------'
echo $-

四、运算符
在 Shell 中,我们不能直接将运算式赋值给变量,这是因为变量值默认都是字符串。此时我们可以使用 $((运算式)) 或 $[运算式] 的方式赋值给变量。

五、条件判断
我们可以使用 test 条件表达式 或 [ 条件表达式 ] 进行条件判断。当条件表达式为真时输出 0,否则输出 1。我们还可以判断条件表达式是否为空,其中非空为真,空为假。
常用的条件判断如下:
- 两个整数之间的比较:
-eq:等于(equal)。-ne:不等于(not equal)。-lt:小于(less than)。-le:小于等于(less equal)。gt:大于(greater than)。-ge:大于等于(greater equal)。
- 按文件权限进行判断:
-r:有读的权限(read)。-w:有写的权限(write)。-x:有执行的权限(execute)。
- 按文件类型进行判断:
-e:文件存在(existence)。-f:文件存在并且是一个常规文件(file)。-d:文件存在并且是一个目录(directory)。

我们还可以进行多条件判断,其中 && 表示 前一条命令执行成功时,才执行后一条命令,|| 表示 上一个命令执行失败后,才执行下一条命令。

六、流程控制
6.1、分支结构
6.1.1、if分支结构
我们可以使用 if 语句执行单分支结构。
if [ 条件判断式 ]; then
程序
fi
或
if [ 条件判断式 ]
then
程序
fi
如果我们要执行多分支逻辑,可以通过以下方式:
if [ 条件判断式 ]
then
程序
elif [ 条件判断式 ]
then
程序
else
程序
fi
#!/bin/bash
if [ $1 -eq 10 ]; then
echo "num == 10"
elif [ $1 -lt 10 ]
then
echo "num < 10"
else
echo "num > 10"
fi

6.1.2、case分支结构
我们可以使用 case 分支结构判断变量的值是否等于某个值。
case $变量名 in
"值1")
如果变量的值等于值1,则执行程序1
;;
"值2")
如果变量的值等于值2,则执行程序2
;;
...省略其它分支...
*)
如果变量的值都不是以上的值,则执行此层序
;;
esac
#!/bin/bash
case $1 in
"1")
echo "the first"
;;
"2")
echo "the second"
;;
"3")
echo "the third"
;;
*)
echo "loser"
;;
esac

我们还可以使用 | 合并多个条件。
#!/bin/bash
case $1 in
"0")
echo "the number is 0"
;;
"1" | "3" | "5" | "7" | "9")
echo "this is an odd number less than 10"
;;
"2" | "4" | "6" | "8")
echo "this is an even number less than 10"
;;
*)
echo "this is other number"
;;
esac

6.2、循环结构
6.2.1、for循环结构
for 循环的语法格式如下:
for ((初始值;循环控制;迭代控制))
do
程序
done
#!/bin/bash
sum=0
for ((i = 0; i <= 10; i++)); do
sum=$[$sum+$i]
done
echo $sum
然后我们在终端中如下如下命令执行 Shell 脚本:
bash template.sh
我们还可以使用如下语法遍历值:
for 变量 in 值1 值2 值3 ...
do
程序
done
#!/bin/bash
for name in sakura mikoto shana
do
echo "$name is a gril"
done
$* 和 $@ 都表示传递给函数或脚本的所有参数,不被双引号包含时都以 $1 $2 ... $n 的格式输出所有参数。但是如果用双引号括起来,"$*" 表示一个变量,"$@" 分为多个变量。
#!/bin/bash
echo '---------- $* ----------'
for name in $*
do
echo "$name is gril"
done
echo '---------- $@ ----------'
for name in $@
do
echo "$name is gril"
done
echo '---------- "$*" ----------'
for name in "$*"
do
echo "$name is gril"
done
echo '---------- "$@" ----------'
for name in "$@"
do
echo "$name is gril"
done

6.2.2、while循环结构
while 循环结构的语法如下:
while [ 条件表达式 ]
do
程序
done
#!/bin/bash
i=1
sum=0
while [ $i -le 100 ]
do
sum=$[$sum+$i]
i=$[$i+1]
done
echo $sum
然后我们在终端中如下如下命令执行 Shell 脚本:
bash template.sh
七、控制台输入
我们可以使用 read 命令读取控制台输入
read [选项] 参数
read 命令可选的选项如下:
\p:指定读取值时的提示词。\t:指定读取值等待的时间(单位为秒),如果 -t 不加表示一直等待。
#!/bin/bash
read -t 7 -p "Pleases enter your name within 7 seconds:" name
echo "Hello $name"

八、函数
在 Shell 中,我们可以自定义函数,它的格式如下:
function 函数名()
{
函数体;
return 函数返回值
}
其中,function 表示是这是一个函数,可以省略。
Shell 脚本是逐行运行的,因此在调用函数之前,必须先声明函数。Shell 脚本中,函数的返回值只能通过 $? 系统便两个获得,可以显示使用 return 函数返回值(函数的返回值只能是 0 到 255 之间的数值,其中 0 表示运行成功,其它值都表示错误)的方式返回。如果不显示的返回,则将以最后一条命令运行结果作为函数返回值。
#!/bin/bash
function sum()
{
s=$[$1+$2]
echo $s
}
read -p "Please enter the first number:" n1
read -p "Please enter the second number:" n2
sum $n1 $n2

九、Shell工具
9.1、cut工具
cut 工具用来在文件中剪切数据,cut 命令从文件中每一行剪切字节、字符和字段并将这些字节、字符和字段输出。它的基本用法如下:
cut [选项] 文件名
其中,它的选项可选值如下:
-f 列号:提取第几列。-d 分隔符:分隔符,按照指定分隔符分割列,默认是制表符\t。-c n:按字符进行切割,n 表示取第几列。

9.2、awk工具
awk 是一个强大的文本分析工具,它把问嗯逐行的读入,以空格为默认的分隔符将每行切片,切开的部分在进行分析处理。
awk [选项] '/匹配模式/{匹配时所要执行的一系列命令}' 文件名
其中,选项的可选值如下:
\F:指定输入文件的分隔符。\v:赋值一个用户定义变量。
匹配模式 如果省略,则 awk 将对所有行进行操作。匹配时所要执行的一系列命令 如果省略,则默认动作是打印整行。

我们还可以使用 BEGIN{} 和 END{} 在匹配时所要执行的一系列命令添加前置操作和后置操作。

awk 还内置了一些变量,例如:
FILENAME:文件名。NR:以读的记录数(行号)。NF:浏览记录的域的个数(切割后,列的个数)。
awk -F : '{print "filename: " FILENAME ", row:" NR ", col:" NF}' /etc/passwd


浙公网安备 33010602011771号