Shell脚本和编程
1. 学习Shell的价值
- Linux服务器的基本操作和管理
- 前端node.js 服务的进程管理、问题排查、资源监控等运维操作
使用 shell 编写 TCE、SCM、Docker 脚本,完成服务编译和部署
Linux很多指令都需要 Shell 完成
2. 学习环境依赖
- 一台安装了 Linux 系统的物理机或者云主机,可运行 Shell 脚本
- 本地的VScode 安装 Bash DeBug 插件,并且升级 bash 到 4.X 以上
- NPM全局安装 zx 依赖
3. Shell构成
shell官方对Shell的定义是命令行解释器,也是一门编程语言。
Shell 作为解释器
- 可以解释脚本和命令
- 同时bash 内置了命令
- GNU 提供了很多工具,如: is 、mv、cat等
- 第三方库:社区或自己编写的一些库,如:node、Python等封装的模块
Shell 作为编程语言
- 变量:可以自定义变量、使用变量的能力
- 运算:可以进行逻辑运算和算数运算
- 语句:判断、分支、循环等编程语言具备的特点
- 函数:定义函数和使用函数的能力
4. Hello Shell
- 在Linux中创建文件test.sh
- 在文件中编辑如下内容
#! /bin/bash
echo "Hello Shell!"
- 代码中第一行:#! 是一个约定的标记 表示告诉系统其后路径所指定的程序即是解释此脚本文件的 Shell 程序,人们并不区分 Bourne Shell 和 Bourne Again Shell,所以,像 #!/bin/sh,它同样也可以改为 #!/bin/bash。
- 第二行:echo 表示输出显示打印 引号内表示要输出的语句
5. 执行 Shell 脚本
要想执行Shell脚本,首先要看看对应的shell脚本文件是否拥有执行权限,可以使用 ll 命令查看,若没有执行权限,可以使用
chmod +x <shell脚本文件名>
- 执行脚本命令
# 中间不可以有空格
./<文件名.sh>
- 执行脚本命令二
sh <shell脚本文件名>
6. Shell的语法
1. Shell变量
2. declare [ + / - ] <选项> <变量>的参数
3. Shell自定义变量
# 变量名=变量值(等号左右不可以有空格,否则shell会当成一个命令)如:
name="张三"
age=21
score=100
# 将命令复制给变量
_ls=ls
# 将命令结果赋值给变量
files=$(ls -a)
# Shell默认的数据类型是字符串,如下面代码不会进行数学运算
num=age*score
# 数学运算需要声明变量为整型
let nums=age*score
declare -i nums=age*score
4. Shell变量的使用
在shell中使用 $ 符号对已经声明的变量进行使用
#! /bin/bash
size=2
page_num=3
let nums=size+page_num
echo "size="$size
echo "page_num="$page_num
echo "相加得"$nums
# 输出结果
# size=2
# page_num=3
# 相加得5
5. 系统环境变量
系统变量可以直接在linux中进行使用直接使用如:echo $HOME
6. Shell 运算符
7. Shell 传递参数
在执行Shell脚本时,可以给Shell脚本传递参数,脚本内获取参数格式为$n 其中 n 代表参数传递过来的是第几个参数,如有下面shell脚本
#! /bin/bash
echo "第一个参数$0"
echo "第二个参数$1"
echo "第三个参数$2"
echo "第四个参数$3"
加入Shell脚本文件名为test.sh脚本执行的时候可以传参,传参方式如下:
sh test.sh 1 2 3 4
# 执行结果
第一个参数test2.sh
第二个参数1
第三个参数2
第四个参数3
总结:
我们会发现其中 $0 获取的是执行的shell文件名称,而自动忽略了第四个参数不显示,所以在Shell脚本中,若参数多的时候会忽略
8. Shell特殊字符处理参数
参数处理 | 说明 |
---|---|
$# | 传递到脚本的参数个数 |
$* | 以一个单字符串显示所有向脚本传递的参数。 如"$*"用「"」括起来的情况、以"$1 $2 … $n"的形式输出所有参数。 |
$$ | 脚本运行的当前进程ID号 |
$! | 后台运行的最后一个进程的ID号 |
$@ | 与$*相同,但是使用时加引号,并在引号中返回每个参数。 如"$@"用「"」括起来的情况、以"$1" "$2" … "$n" 的形式输出所有参数。 |
$- | 显示Shell使用的当前选项,与set命令功能相同。 |
$? | 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 |
1.Shell脚本特殊字符处理参数($*)和($@)的区别
如:test.sh 文件中有如下代码
#!/bin/bash
echo "-- \$* 演示 ---"
for i in "$*"; do
echo $i
done
echo "-- \$@ 演示 ---"
for i in "$@"; do
echo $i
done
则输出结果
-- $* 演示 ---
1 2 3
-- $@ 演示 ---
1
2
3
我们可以从输出中看到$*
会将传送的变量当作一个整体,而$@
会将传输的参数散开,当成三个参数使用
7. Shell输入输出重定向
Shell 分为标准输入(fd0),标准输出(fd1),标准错误输出(fd2)
- 输出重定向符号
符号 | 含义 |
---|---|
> | 覆盖式写入文件 |
>> | 追加写入文件 |
2> | 错误输出写入文件 |
&> | 正确和错误输出统一写入文件中 |
- 输出重定向符号
符号 | 含义 |
---|---|
< | |
<< |
需要注意的是文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。
8. Shell判断命令
- Shell 中提供了
test
、[
、[[
三种判断符号,可以使用到
- 整数判断
- 字符串测试
- 文件测试
- 语法:
- test <参数>
- [ <参数> ]
- [[ <参数> ]]
注意:
中括号前后要有空格符
[ 和 `test` 命令只能使用自己支持的标志位,<、>、= 只能用来比较字符串,不能比较整数
中括号内的变量,最好都使用引号括起来
[[ 更丰富,在整形比较中,支持<、>、= 在字符串中支持 =~ 正则
- 测试代码
#! /bin/bash
# 整数测试
test $n1 -eq $n2 # 两个整数变量等于
test $n1 -lt $n2 # 两个整数变量小于
test $n1 -gt $n2 # 两个整数变量大于
# 字符串测试
test -z $str # 判断字符串为空 -z
test -n $str # 判断字符串非空 -n
test $ str1 = $str2 # 判断字符串相等 =
# 文件测试
test -e /dmt && echo "exist" # 文件是否存在 -e
test -f /usr/bin/npm && echo "file exist" # 文件是否存在,并且是一个普通文件
9. Shell 条件判断
if <条件>
then
代码块
elif <条件>
then
代码块
else
代码块
fi
判断两个变量是否相等
a=10
b=20
if [ $a == $b ]
then
echo "a 等于 b"
elif [ $a -gt $b ]
then
echo "a 大于 b"
elif [ $a -lt $b ]
then
echo "a 小于 b"
else
echo "没有符合的条件"
fi
如果使用 ((.....))
a=10
b=20
if (( $a == $b ))
then
echo "a 等于 b"
elif (( $a > $b ))
then
echo "a 大于 b"
elif (( $a < $b ))
then
echo "a 小于 b"
else
echo "没有符合的条件"
fi
10.Shell函数
- 语法一
funName(){
方法体
}
- 语法二
function funName(){
方法体
}
使用方法:
function add(){
let sum=$1+$2
echo sum
}
# 调用方式
* add 1 2
# 是不需要写括号的
在Shell中是不需要声明参数的,在方法内使用$进行参数获取
注意:
- Shell 是自上而下执行的,函数定义在前,使用在后
- 函数获取变量和 Shell script类似,$0 代表函数名称
- 函数内return 仅仅表示函数的执行状态,不代表函数的执行结果
- 返回结果一般使用
echo
、printf
,在外边使用$()
、``获取结果 - 如果没有 return 函数的状态是上一条命令的执行状态,存储在
$?
11. 模块化
模块化的原理是在当前的Shell内执行函数文件,引入方式:suorce [函数库的路径]
如,现在有文件test1.sh
#! /bin/bash
function add(){
let sum=$1+$2
echo $res
}
有第二个文件main.sh
#! /bin/bash
source './math.sh'
sum=$(add 1 2)
echo $sum
12. Shell脚本的执行过程
字符解析
- 识别换行符,分号,做行的分割
- 识别命令连接符(|| 、&&、 管道)做命令的分割
- 识别空格,tab符号,做命令和参数的分割
- Shell 展开,如:{1..3} 解析为 1 2 3 后面会详细说
- 重定向,将标准文件的输入输出进行指向的变更
执行命令
- 内置命令会在当前进程直接执行
- 非内置命令会使用$PATH 查找,然后启动子进程执行
- 手机状态并返回
13. Shell 展开
1. 大括号展开
一般由三部分构成,前缀、一对大括号,后缀,大括号内可以是逗号分割的字符串序列,也可以是序列表达式
# 字符序列
a{b,c,d}e 展开结果=> abe ace ade
# 表达式序列 (数字可以使用..调整增量,字母不能调整增量)
{1..5} => 1 2 3 4 5
{1..5..2} => 1 3 5
{a..e} => a b c d e
2. 波浪号展开
波浪号会将当前替换成当前用户主目录的值
~ 展开是 # $HOME就是用户目录
~root => # root指定用户的目录
~+ => # 当前工作目录
~- => # 上一个工作目录
3. 大括号展开
- 间接参数扩展
${!parameter}
,其中应用的并不是parameter 而是 parameter的实际值
parameter="var"
var="hello"
echo ${!parameter}
# 首先会将parameter转换成var
# 其次${var}的值是hello
# 所以最后输出的值是 hello
- 参数长度 ${parameter}
输出parameter的长度
par=cd
echo ${#par}
# 输出 2
空参数处理
- ${parameter:-word} 为空替换 判断parameter的值是否为空,为空使用word替换
- ${parameter:=word} 为空替换,判断parameter的值是否为空,为空将parameter的值赋值为word
- ${parameter:?word} 为空报错
- ${parameter:+word} 不为空替换
如,有下面Shell脚本
a=1
echo ${a:-wrod} # 1
echo ${b:-wrod} # word
echo ${par:=wrod} # word
echo ${par:-hello} # word 因上一条语句已经赋值为word,所以不为空,所以输出结果还是word
echo ${a:+foo} # foo
- 参数切片
${parameter:offset}
${parameter:offset:lenth}
参数部分删除,对字符掐头去尾的操作
- ${parameter%word} # 最小限度从后面截取word
- ${parameter%%word} # 最大限度从后面截取word
- ${parameter#word} # 最小限度从前面截取word
- ${parameter##word} # 最大限度从前面截取word
#! /bin/sh
str=abcdefg
sp1=${str##*d}
sp2=${str%%d*}
echo $sp1 # 输出efg
echo $sp2 # 输出abc
14. Shell脚本VScode好用插件推荐
- Shellman :代码提示和自动补全
- shellcheck:代码语法校验
- shell-format : 代码格式化
Bash Debug 支持单步调试
- 首先要安装vscode插件
- 编写launch.json文件
- 升级bash到4.x以上的版本