Shell 脚本和编程 | 青训营笔记
介绍
- Shell 脚本是一种用Shell脚本语言编写的程序,它可以在 UNIX 或类 UNIX 操作系统上运行。Shell 脚本通常用于自动化任务、管理系统和编写小型应用程序。Shell 脚本可以完成许多任务,例如文件操作、进程管理、网络管理、系统配置等。
- Shell 脚本语言基于命令行界面,它使用 Shell 解释器(例如 Bash)解释 Shell 脚本语言。
- Shell 脚本语言的语法非常简单,可以包含各种命令和操作,例如条件语句、循环语句、函数、变量和输入/输出操作等,易于学习和使用。
学习 shell 的价值
- Linux 服务器的基本操作和管理。
- 前端 node.is 服务的进程管理、问题排查、资源监控等运维操作。
- 使用 shell 编写 TCE、SCM、Docker 脚本,完成服务编译和部署。
概念
物理终端 => 软件终端 tty => 终端模拟器 => Shell
- 终端:获取用户输入,展示运算结果的硬件设备。
- tty:teletypeWriter 的简称,和终端等价,早期指电报打印机,在 linux 中是输入输出环境。
- 终端模拟器:Mac Terminal、iTerm2 等,关联虚拟 tty 的输入输出软件。
- Shell:command interpreter,处理来自终端模拟器的输入,解释执行之后输出结果给终端。
- Bash: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 的功能,包括命令历史,命令行编辑,目录堆栈(pushd 和 popd),一些实用环境变量,命令自动补全等。
语法
- shell 不仅提供了与内核和设备交互的方法,还集成了一些今天软件开发中通用的设计模式(比如管道和过滤器),具备控制流程,循环,变量,命令查找的机制。
- 既是命令解释器,也是一门编程语言,作为命令解释器,它提供给用户接口,使用丰富的 GNU 工具集,第三方的或者内置的,比如 cd、pwd、exec、test、netstat 等。
变量
父子 Shell 的关系
自定义变量
declare [+/-] 选项 变量
环境变量
Bash Shel 在启动时总要配置其运行环境,例如初始化环境变量、设置命令提示符、指定系统命令路径等。
配置文件加载
- 通过系统用户登录默认运行的 shell
- 非登录交互式运行 shell
- 执行脚本运行非交互式 shell
如果取得 bash 需要完整的登录流程,我们称之为 login shell,比如 ssh 远程登录一台主机。不需要登录的 bash 我们称为 non-login bash,比如在原来的 bash 中执行 bash开启子进程、 执行一些外部命令。如果修改了配置文件,不会立即生效,需要我们重启终端或者执行 source 命令。
source ~/bashrc
运算符和引用
- 双引号:部分引用,使用这种引用时,
$
、`(反引号)、\(转义符) 这 3 个还是会解析成特殊的意义 - 单引号:完全引用,只原样输出
- 反引号:执行命令
cmd & 实现让命令在后台运行:
使用方法一的时候,当我们关闭终端,命令就会停止运行。加上nohup可以在关闭终端后不停止命令
管道与管道符 |
如果需要互通,比如第一个命令的返回传递给第二个命令,就需要用到管道了。管道的本质就是将多个程序进行了一个连接,和信号一样,也是进程通信的方式之一。
- 作用:将前一个命令的结果传递给后面的命令
- 语法: cmd1 | cmd2
- 要求:管道右侧的命令必须能接受标准输入才行,比如 grep 命令,ls、mv等不能直接使用,可以使用 xargs 预处理注意: 管道命令仅仅处理 stdout,对于 stderr 会予以忽略,可以使用 set-o pipefail 设置 shell 遇到管道错误退出
重定向
输出重定向符号:
v
:覆盖写入文件>>
:追加写入文件2>
:错误输出写入文件&>
:正确和错误输出统一写入到文件中
输入重定向符号:>
和 >>
。
每个 shell 命令在执行时都会打开三个文件描述符, 文件描述符 0、1、2, 分别对应 stdin、stdout、stderr, 这三个文件描述符默认默认指向 终端输入、终端输出,那么当命令需要获取输入的时候,它会去读取 fd0, 当要输出的时候它会像 fd1、fd2写入, 改变这些描述符指向的行为叫做重定向
2>&1 必须写在 > 之后
<< 比较特殊, 表示继续沿用当前的标准输入, 只是当识别到指定的标识符后停止, 将接收到的内容作为 stdin
实例: 用户在命令行输入内容,当输入 EOF 的时候停止, 所输入的内容写入 foo.txt
判断命令
shell 中提供了 test
、[
、[[
三种判断符号,可用于:
- 整数测试
- 字符串测试
- 文件测试
语法:
test condition
[ condition ]
[[ condition ]]
注意:
- 中括号前后要有空格符
[
和test
是命令只能使用自己支持的标志位,<
、>
、=
只能用来比较字符串- 中括号内的变量,最好都是用引号括起来
[[
更丰富,在整型比较中支持<
、>
、=
,在字符串比较中支持=
、~
正则
根据程序是否正常执行(程序退出的状态)进行判断:
- exit:手动退出 shell 的命令
- exit 10 返回 10 给 shell,返回值非 0 为不正常退出
- $? 用于判读昂当前 shell 前一个进程是否正常退出(非 0 为不正常退出)
分支语句
语法1:
if condition ;then
程序段
elif condition ; then
程序段
esle
程序段
fi
语法2:
case $变量 in:
"第一个变量内容")
程序段
;;
"第一个变量内容")
程序段
;;
*)
程序段
esac
循环
-
while循环
condition ; do 程序段; donewhile
-
unti1循环(当条件成立的时候跳出循环,与 while循环相反)
until condition ; do 程序段; done
-
for循环
for var in [words...];do 程序段:done
函数
-
语法一:
funcName() echo "abc";
-
语法二:
function funcName() { echo "abc"; }
-
函数也是命令
- exit:手动退出 shell 、命令
- exit 10 返回 10 给 shell,返回值非 0 为不正常退出
- $? 用于判读昂当前 shell 前一个命令是否正常退出(非 0 为不正常退出)
为了函数内定义的变量不污染全局, 我们最好使用 local 去定义, 或者在函数退出之前使用 unset 去处理一下
注意:
- shell 自上而下执行,函数必须在使用前定义
- 函数获取变量和 shell script 类似,$0 代表函数名, 后续参数通过 $1、$2 ...获取
- 函数内 retun 仅仅表示函数执行状态,不代表函数执行结果
- 返回结果一般使用 echo、printf, 在外面使用 $0 获取
- 结果如果没有 return ,函数状态是上一条命令的执行状态,存储在 $? 中
模块化
模块化的原理是在当前 she11 内执行函数文件,方式:source [函数库的路径]
。
常用命令
执行
- shell 脚本文件一般以
.sh
结尾,也可以没有,这是一个约定; 第一行需要指定用什么命令解释器来执行。#!
是内核识别并选择合适的解释器之后,将文本文件再交给解释器执行。
- 启动方式:
Shell 展开
- 大括号展开 (Brace Expansion) {...}
- 波浪号展开 (Tilde Expansion) ~
- 参数展开 (Shell Parameter Expansion)
- 命令替换 (Command Substitution)
- 数学计算 (Arithmetic Expansion) $((..))
- 文件名展开 (Filename Expansion) *?[..] 外壳文件名模式匹配
调试和前端集成
- 普通 log,使用 echo、printf
2. 使用 set 命令
3. vscode debug 插件
-
node
-
node中通过 exec、spawn 调用 shell 命令
-
shell 脚本中调用 node 命令
-
借助 zx 等库进行 javascript、 shell script 的融合
- 借助 shell 完成系统操作,文件io、内存、磁盘系统状态查借助 nodejs 完成应用层能力, 网络io、计算等
- 借助 shell 完成系统操作,文件io、内存、磁盘系统状态查借助 nodejs 完成应用层能力, 网络io、计算等
-
exec 启动一个子 shell 进程执行传入的命令,并且将执行结果保存在缓冲区中, 并且缓冲区是有大小限制的,执行完毕通过回调函数返回,
spawn 默认不使用 shell,而是直接启动子进程执行命令,且会直接返回一个 流对象,支持写入或者读取流数据,这个在大数据量交互的场景比较适合
总结
shell 的思想和语法和传统的编程语言不太一样,强调一条语句只干一件事,所以万物皆命令, 在执行过程中也是逐行、逐个连接符、逐个空格的解析出最小化的命令执行,执行完之后再解析下一句。了解 shell 的配置加载、执行方式、执行过程、命令解析过程、 必要的语法、常用命令,可以方便的写出自己的自动化脚本。