这是我参与「第五届青训营 」笔记创作活动的第3天
Shell脚本和编程
1 课程介绍与准备
2 Shell 基础概念
3 语法和命令
4 执行过程和原理
5 调试和前端集成
6 引用参考
课程介绍与准备
学习shell的价值
Linux 服务器的基本操作和管理
前端 Node.js 服务的进程管理 、问题排查 、资源监控等运维操作
使用 shell 编写 TCE 、SCM 、Docker 脚本 ,完成服务编译和部署
课程准备
一台安装了 linux 系统的物理机或者云主机 ,可运行 shell 脚本
本地的 vscode 安装 Bash Debug 插件 ,并升级 bash 到 4.x 以上
Npm 全局安装 zx 依赖
Shell 基础概念
概念
物理终端 => 软件终端 tty => 终端模拟器 => shell
tty 或者说终端最开始指的是获取用户输入并输出的物理设备 , 比如电传打字机
在 Linux 中是接收用户输入 、输出结果的终端仿真软件 , 比如我们用的 mac terminal 、iterm2 等 , 更强输入辅助功能 、画面绘制输出的模拟终端器 ;而 tty 变成一个虚拟概念 , 是 Linux 的一个程序 ,每个终端模拟器关联一个虚拟 tty ,和内核打交道 。 我们可以在终端模拟器中输入 tty 查看关联到的虚拟 tty
bash 是 shell的一种具体实现 , 可以理解成实例和类的关系
发展
- Ken Thompson 在 1971 年为 UNIX 开发了第一个 shell , 称为 V6 shell
- Stephen Bourne 在贝尔实验室为 V7 UNIX 所开发的 Bourne shell , 即 sh
- 开源组织 GNU 为了取代 Bourne shell 开发的 Bourne-Again shell , 即 Bash
除了替代 v6 shell ,sh 还有几个优点 ,把控制流程 ,循环 ,变量引入了脚本 ,提供了一种更具功能性的语言
主流 Linux 系统使用的 shell ,许多都以它为锚点
bash是 sh 的超集 ,可以直接执行大部分 sh 脚本
Bash 在兼容 Bourne shell 脚本编程的同时 ,集成了 Korn shell 和 C shell 的功能,包括命令历史 ,命令行编辑 ,目录堆栈 ,一些实用环境变量 ,命令自动补全等
构成
shell 不仅提供了与内核和设备交互的方法 ,还集成了一些今天软件开发中通用的设计模式(比如管道和过滤器), 具备控制流程 ,循环 ,变量 , 命令查找的机制
既是命令解释器 , 也是一门编程语言 , 作为命令解释器 , 它提供给用户接口 ,使用丰富的 GNU 工具集 , 第三方的或者内置的 , 比如 cd 、pwd 、exec 、test 、 netstat 等等
语法和命令
语法
变量
-
自定义变量
-
系统环境变量
配置文件加载
通过系统用户登录默认运行的 shell
非登录交互式运行 shell
执行脚本运行非交互式 shell如果取得 bash 需要完整的登录流程 , 我们称之为 login shell, 比如 ssh 远程登录一台主机
不需要登录的 bash 我们称为 non-login bash , 比如在原来的 bash 中执行 bash 开启子进程 、 执行一些外部命令
如果修改了配置文件 ,不会立即生效 ,需要我们重启终端或者执行 source 命令
运算符和引用
双引号:部分引用,使用这种引用时,$、`(反引号)、(转义符) 这 3 个还是会解析成特殊的意义
cmd & 实现让命令在后台运行使用方法一的时候,当我们关闭终端,命令就会停止运行。加上nohup可以在关闭终端后不停止命令管道
管道与管道符 | ,作用是将前一个命令的结果传递给后面的命令 语法:cmd1 | cmd2
要求:管道右侧的命令必须能接受标准输入才行 ,比如 grep 命令 ,ls 、mv 等不能直接使用 ,可以使用 xargs 预处理
注意:管道命令仅仅处理 stdout , 对于 stderr 会予以忽略 ,可以使用 set -o pipefail 设置 shell 遇到管道错误退出
重定向
输出重定向符号
>:将命令的输出重定向到一个文件中 ,如果文件不存在则创建 ,如果文件已经存在则覆盖
>>:将命令的输出重定向到一个文件中 ,如果文件不存在则创建 ,如果文件已经存在则在文件末尾追加
2>:将命令的错误输出重定向到一个文件中 ,如果文件不存在则创建 ,如果文件已经存在则覆盖
&>:将命令的输出和错误输出重定向到一个文件中 ,如果文件不存在则创建 ,如果文件已经存在则覆盖输入重定向符号
<:将一个文件的内容作为命令的输入
<<:将一段字符串作为命令的输入
命令
-
判断
shell中提供了test、[、[[三种判断符号,可用于:
整数测试
字符串测试
文件测试
语法:
test condition
[ condition ]
[[ condition ]]
注意:
中括号前后要有空格符
[ 和test是命令 ,只能使用自己支持的标志位,< 、> 、= 只能用来比较字符串
中括号内的变量 ,最好都是用引号括起来
[[更丰富,在整型比较中支持< 、> 、= ,在字符串比较中支持 =~ 正则
根据程序是否正常执行(程序退出的状态)进行判断
exit:手动退出 shell 的命令
exit 10 返回 10 给 shell ,返回值非 0 为不正常退出
$? 用于判读昂当前 shell 前一个进程是否正常退出(非 0 为不正常退出)
-
分支
1: if 语句
if语句用于判断条件是否成立 ,如果成立则执行相应的命令或者代码块
2: case 语句
case语句用于根据不同的条件执行相应的命令或者代码块
-
循环
1: while 循环
while 循环用于在条件成立的情况下重复执行一段代码块
2: until 循环
until 循环与 while 循环类似 ,不同之处在于 until 循环在条件不成立的情况下重复执行一段代码块
3: for 循环
for 循环用于遍历一组数据 ,并对每个数据执行相同的命令或者代码块
-
函数
在 shell 中 ,函数是一段可以重复使用的代码块 ,可以在脚本中定义并调用
注意
shell自上而下执行 ,函数必须在使用前定义
函数获取变量和 shell script 类似 ,0 代表函数名 ,后续参数通过 0 代表函数名 ,后续参数通过 0 代表函数名 ,后续参数通过 1 、$2 ... 获取
函数内return仅仅表示函数执行状态 ,不代表函数执行结果
返回结果一般使用 echo 、printf ,在外面使用 $() 、`` 获取结果
如果没有 return ,函数状态是上一条命令的执行状态 ,存储在 $?中
函数也是命令
exit:手动退出 shell 、命令
exit 10 返回 10 给 shell ,返回值非 0 为不正常退出
$? 用于判读昂当前 shell 前一个命令是否正常退出(非 0 为不正常退出)
为了函数内定义的变量不污染全局 , 我们最好使用 local 去定义 , 或者在函数退出之前使用 unset 去处理一下
-
模块化
模块化的原理是在当前shell内执行函数文件,方式
source[函数库的路径]
常用命令
执行过程和原理
执行
- shell 脚本一般以 .sh 结尾 ,也可以没有 ,这是一个约定 ;第一行需要指定用什么命令解释器来执行
#! /bin/bash
#! /user/bin/env bash - 启动方式
#文件名运行(子进程执行) : ./filename.sh
#解释器运行(子进程执行) : bash ./filename.sh
#source 运行(当前进程中执行) : source ./filename.sh
#!是内核识别并选择合适的解释器之后,将文本文件再交给解释器执行
执行过程
-
字符解析
识别换行符 、分号 (;) 做行的分割
识别命令连接符 (||&&管道) 做命令的分割
识别空格 、tab 符 ,做命令和参数的分割 -
shell展开 ,例如 {1...3} 解析为 1 2 3
-
重定向,将stdin、stdout、stderr的文件描述符进行指向变更
-
执行命令
builtin 直接执行
非 builtin 使用 $PATH 查找 ,然后启动子进程执行 -
收集状态并返回给脚本
shell 展开
Shell 展开是指在执行命令之前,Shell 预处理命令行中的各种特殊字符,将它们替换为实际的值或执行相应的操作
- 大括号展开
一般由三部分构成 ,前缀 、一对大括号 、后缀 ,大括号内可以是逗号分割的字符串序列 ,也可以是序列表达式 {x..y[..incr]} - 波浪号展开
- 参数展开
(1) 间接参数扩展 {#parameter}
(3) 空参数处理
${parameter:-word} # 为空替换
${parameter:=word # 为空替换 ,并将值赋给 parameter 变量
$parameter:?word) # 为空报错
%{parameter:+word} # 不为空替换
(4) 参数切片
{parameter:offset) {parameter:offset:length}
(5) 参数部分删除
${parameter%word} # 最小限度从后面截取 word
${parameter%%word} # 最大限度从后面截取 word
${parameter#word} # 最小限度从前面截取 word
${parameter##word} # 最大限度从前面截取 word
- 命令替换
在子进程中执行命令 ,并用得到的结果替换包裹的内容 ,形式上有两种:$(...) 或 ... - 数学计算
使用$( ( ) ) 包裹数学运算表达式,得到结果并替换 - 文件名展开
当有单词没有被引号包裹 ,且其中出现了 ‘*’ , ‘?’ , and ‘[’ 字符 ,则 shell 会去按照正则匹配的方式查找文件名进行替换 ,如果没找到则保持不变
调试的前端集成
调试
- 普通 log , 使用 echo 、printf
- 使用 set 命令
- vscode debug插件
shellman:代码提示和自动补全
shellcheck:代码语法校验
shell-format:代码格式化
Bash Debug:支持单步调试
安装vscode插件
编写launch.json文件
升级bash到4.x以上版本
前端集成
- node中通过exec、spawn调用shell命令
- shell脚本中调用node命令
- 借助zx等库进行javascript、shell script的融合
借助shell完成系统操作,文件io、内存、磁盘系统状态查看
借助nodejs完成应用层能力,网络io、计算等
exec 启动一个子 shell 进程执行传入的命令 , 并且将执行结果保存在缓冲区中 ,并且缓冲区是有大小限制的 ,执行完毕通过回调函数返回
spawn 默认不使用 shell , 而是直接启动子进程执行命令 ,且会直接返回一个流对象 ,支持写入或者读取流数据 ,这个在大数据量交互的场景比较适合
后言
其实这里我已经几乎都听不懂了
学习途中就要参考其他的学习视频了
看来前端的学习还任重道远 , 还要继续努力
最后也是祝大家 : 学业有成 , 职场顺利 , 专业能力越来越强 , 拿到自己满意的 offer !
引用参考
字节前端训练营 Shell脚本和编程学习资料