Shell 脚本和编程
笔者:YDSUPER
课程地址:点我前往
零、课程介绍
- Linux服务器的基本操作和管理
- 前端 Node.js 服务的进程管理、问题排查、资源监控等运维操作
- 使用 shell 编写 TCE、SCM、Docker 脚本,完成服务编泽和部署
一、Shell 基础概念
概念
-
终端 获取用户输入、展示运输结果的硬件设备
-
tty teletypeWriter的简称,和终端等价,早期指电传打印机,在linux中是 输入/输出 环境
-
终端模拟器 Mac Terminal、iTerm2等,关联虚拟tty的输入输出软件
-
Shell
command interpreter,处理来自终端模拟器的输入,解释执行之后输出结果给终端
-
Bash
shell的一种具体实现
发展
- Ken Thompson (来自贝尔实验室)在1971年为 UNIX 开发了第一个she11,称为V6 shell
- Stephen Bourne 在贝尔实验室为V7 UNIX所开发的 Bourne shell,即 sh
- 开源组织 GNU 为了取代 Bourneshe11 开发的 Bourne-Again shell,即 Bash
构成
二、命令和语法
变量
| 类型 | 作用域 | 声明方式 | 规范 |
|---|---|---|---|
| 自定义变量 | 当前 shell | = | 字符串、整型、浮点型、日期型 |
| 环境变量 | 当前 shell 及其子 shell | export、declare -x | |
| 系统环境变量 | 所有 shell | 启动加载 |
自定义变量
| declare [+/-] 选项 变量 |
|---|
系统环境变量
| 变量名 | 含义 | 常见操作 |
|---|---|---|
| $0 | 当前shell名称/脚本名称 | 2等可以获取到传入参数 |
| $# | 传入脚本的参数数量 | if [ $# -gt 1 ] |
| * | 传入脚本的所有参数 | |
| $? | 上条命令执行的状态码 | if [ $? -eq 0 ]; |
| $PS1 | 命令提示符 | export PS1="\u@\h \w> " |
| $HOME | 用户主文件夹 | cd ~ |
| $PATH | 全局命令的搜索路径 | PATH=$SPATH:[新增路径] |
配置文件加载
source ~/.bashrc #执行脚本 生效
运算符和引用
| 类型 | 符号 | 作用 | 用法 |
|---|---|---|---|
| 算数运算符 | + - * / % | & | 常规运算 | |
| 逻辑运算符 | || && ! | ||
| 比较运算符 | == != < > | ||
| 引号 | 双引号 “” | 部分引用,仅仅$、\保留作用 | foo="$ {a} 123" |
| 单引号 ‘’ | 完全引用,原样输出 | foo='foo$a' | |
| 反引号 `` | 执行命令 | foo= `1s - a` | |
| 圆括号 | (()) | 算数运算 | foo=$((1+2)) |
| () | 执行命令 | $(1s -a) | |
| 命令连接 | || | cmd1执行完且返回码非0,则继续执行 cmd2 | cmd1 || cmd2 |
| && | cmd1执行完且返回码为0,则继续执行 cmd2 | cmd1 && cmd2 | |
| ; | cmd1、cmd2串行执行 | cmd1 ; cmd2 | |
| 后台运行 | & | 让命令在后台运行,可与nohup一起使用 | cmd & |
管道
管道与管道符 |,作用是将前一个命令的结果传递给后面的命令
-
语法:cmd1 | cmd2
-
要求:管道右侧的命令必须能接受标准输入才行,比如grep命令,Is、mv等不能直接使用,可以使用xargs预处理
-
注意:管道命令仅仅处理stdout,对于stderr会予以忽略,可以使用set-o pipefail设置shell 遇到管道错误退出
重定向
-
输出重定向符号
>:覆盖写入文件
>>:追加写入文件 2>:错误输出写入文件 &>:正确和错误输出统一写入到文件中
-
输入重定向符号
<
<<
判断命令
shell 中提供了 test、[、[[ 三种判断符号,可用于:
- 整数测试
- 字符串测试
- 文件测试
语法:
- test condition
- [ condition ]
- [[ condition ]]
# 整数测试
test $n1 -eq $n2
test $n1 -lt $n2
test $n1 -gt $n2
# -eg/-lg/-gt 分别代表:等于/小于/大于
# 字符串测试
test -z $str_a #判断是否为空
test -n $str_a #判断是否非空
test $str_a = $str_b #判断是否相对
# 文件测试
test -e /dmt && echo "" #判断文件是否存在 并在存在时输出 "exist
test -f /user/bin/npm && echo "file exist" #判断文件是否存在且是一个普通文件
注意:
- 中括号前后要有空格符
- [ 和 test 是命令,只能使用自己支持的标志位,<、>、= 只能用来比较字符串
- 中括号内的变量,最好都是用引号括起来
- [[ 更丰富,在整型比较中支持 <、>、=,在字符串比较中支持 =~ 正则
#! /bin/bash
name="hello world"
[ $name == "hello" ] ❌ 会被解析成:[ hello world == "hello" ] #字符串没带上引号
[ "$name" == "hello" ] ✅
分支语句
语法1:
if condition; then
程序段
elif condition; then
程序段
else
程序段
fi
语法2:
case $变量 in:
"第一个变量内容")
程序段
;;
"第二个变量内容")
程序段
;;
*)
程序段
;;
esac
循环
-
while 循环
while condition ; do 程序段; done -
until 循环
until condition ; do 程序段; done -
for 循环
for var in [words...]; do 程序段; done# 对列表进行循环 for foo in a b c do echo $foo done # 数值方式循环 for((i=0;i<10;i++)) do echo $i done
函数
# 语法一
funcName(){ echo "xxx"; }
# 语法二
funciton funcName() { echo "xxx"; }
# 调用
funcName aaa bbb
# 1.空格间隔传参
# 2.内部通过 $1 $2... 接收参数
# 扩展
函数内使用 local 定义变量,避免全局污染
也可以最后使用 unset 变量 将变量撤销掉
注意:
- shell 自上而下执行,函数必须在使用前定义
- 函数获取变量和 shell script 类型,1、$2 ...获取
- 函数内 return 仅仅表示函数执行状态,不代表函数执行结果
- 返回结果一般使用 echo、printf,在外面使用 $()、``获取结果
- 如果没有 return,函数状态是上一条命令的执行状态,存储在 $? 中
模块化
模块化的原理是在当前 shell 内执行函数文件,方式:
source [函数库的路径]
常用命令
| 命令 | 使用 |
|---|---|
| grep | 查找错误日志:grep -n "ERROR" -A3 -B3 cloudfun.log统计次数: grep -n "ERROR" -c cloundfun.log |
| sort | 指定分隔符后以第三列进行排序:sort -t " " -k 3 |
| wc | 统计出现的行数、单词数、字符数:wc -lwm |
| head | 查看前十行:head -n 10 cloudfun.log |
| tail | 等待追加内容:tail -f -n 10 cloudfun.log |
| cut | 对数据行的内容进行处理:cut -d " " -f 3 |
| find | 文件和目录查找 |
| xargs | 参数处理 |
| which | 查找命令路径 |
三、执行过程和原理
执行
-
shell 脚本一般以
.sh结尾,也可以没有,这是一个约定;第一行需要指定用什么命令解释器来执行#! /bin/bash#! /user/bin/env bash -
启动方式
# 文件名运行 ./filename.sh # 解释器运行 bash ./filename.sh # source 运行 source ./filename.sh
执行过程
- 字符解析
- shell 展开,例如 {1..3} 解析为:1 2 3
- 重定向,将stdin、stdout、stderr的文件描述符进行指向变更
- 执行命令
- builtin 直接指向
- 非 builtin 使用 $PATH 查找,然后启动子进程执行
- 收集状态并返回
shell展开
- 大括号展开
{...} - 波浪号展开
~ - 参数展开
- 命令替换
- 数学计算
$((..)) - 文件名展开
*?[..] 外壳文件名模式匹配
四、调式和前端集成
调试
- 普通log,使用echo、printf
- 使用 set 命令
- vscode debug插件
| set 配置 | 作用 | 补充 |
|---|---|---|
| -u | 遇到不存在的变量就会报错,并停止执行 | -o nounset |
| -x | 运行结果之前,先输出执行的那一行命令 | -o xtrace |
| -e | 只要发生错误,就终止执行 | -o errexit |
| -o pipefail | 管道符链接的,只要一个子命令失败,整个管道命令就失败,脚本就会终止执行 |
VSCode 配置
-
shellman:代码提示和自动补全
-
shellcheck:代码语法校验
-
shell-format:代码格式化
-
Bash Debbug:支持单步调试
- 安装 vscode 插件
- 编写 launch.json 文件
- 升级 bash 到 4.x 以上版本
前端集成
- node中通过exec、spawn调用 shell 命令
- shell 脚本中调用 node 命令
- 借助 zx 等库进行 javascript、shell script 的融合
- 借助 shell 完成系统操作,文件io、内存、磁盘系统状态查询
- 借助 nodejs 完成应用层能力,网络io、计算等