shell脚本与编程
01.shell基本概念
01_基础概念
物理终端 => 软件终端tty => 终端模拟器 => shell
(终端与tty是一个等价的概念)
- 最初, tty与终端最开始指的就是获取用户输入并输出的物理设备,如:电传打字机, 现在在Linux中是接收用户输入 输出结果的终端仿真软件
- 终端模拟器关联着虚拟tty,可以在终端模拟器中输入tty查看关联到的tty
- bash是shell的一种具体实现(可以理解为类(shell) 与 实例(bash) 的关系)
01_发展
bash是sh的超集, 可以直接执行大部分的sh脚本
同时 Bash在兼容Bourne shell脚本编程的同时, 集成了ksh 与 csh的功能,包括命令历史、命令行编辑、目录堆栈(pushd 和 popd),一些实用环境变量、命令自动补全等
01_构成
shell不仅提供了与内核和设备交互的方法,还集成了一些如今软件开发中通用的设计模式(如管道和过滤器),具备控制流程、循环、变量、命令查找的机制
即是命令解释器,也是一门编程语言,作为命令解释器,他提供给用户接口,使用丰富的GUN工具,第三方的或者内置的,比如cd、pwd、test、exec、netstat等
02.命令和语法
| 类型 | 作用域 | 声明方式 | 规范 |
|---|---|---|---|
| 自定义变量 | 当前shell | = | 字符串、整型、浮点型、日期型 |
| 环境变量 | 当前shell及其子shell | export、declare -x | |
| 系统环境变量 | 所有shell | 启动加载 |
可以看下图
02_自定义变量
| 选项 | 含义 |
|---|---|
| - | 给变量设定类型 |
| + | 取消变量的类型属性 |
| -a | 将变量声明为数组类型 |
| -i | 将变量声明为整数型 |
| -x | 将变量声明为环境变量 |
| -r | 将变量声明为只读变量 |
| -p | 显示指定变量的被声明的类型 |
02_系统环境变量
| 变量名 | 含义 | 常见操作 |
|---|---|---|
| $o | 当前shell名称 / 脚本名称 | 2等可以获取到传入参数 |
| $# | 传入脚本的参数数量 | if [$#-gt1] |
| $* | 传入脚本的所有参数 | |
| $? | 上条命令执行的状态码 | if[$?-eqo]; |
| $PS1 | 命令提示符 | export PS1-"\u@\h\w>" |
| $HOME | 用户主文件夹 | cd~ |
| $PATH | 全局命令的搜索路径 | PATH=$PATH:[新增路径] |
02_配置文件加载
- login shell:取得bash需要完整的登录流程
- non-login bash:ssh远程登录一台主机不需要登录的bash
02_运算符及其引用
02_管道
管道与管道符 | ,作用是将前一个命令的结果传递给后面的命令
语法: cmd1 | cmd2
要求: 管道右侧的命令必须能接受标准输入才行,比如frep命令,ls、mv等不能直接使用,可以使用xargs预处理
注意: 管道命令仅仅处理stdout,对于stderr会予以忽略,可以使用set -o pipefail设置shell遇到管道错误退出
#!/bin/bash
cat platform.access,log | grep ERROR
netstat -an | grep ESTABLISHED | wc -1
find . -maxdepth 1 -name "*.sh" | xargs ls -1
如果需要互通,如一个命令的返回传递给第二个命令,就需要用到管道了,
管道的本质就是将多个程序进行了一个连接,和信号一样,也是进程通信的方式之一
02_重定向
每个shell命令在执行时都会打开三个文件描述符,文件描述符0、1、2分别对应stdin、stdout、stderr,这三个文件描述符默认指向终端输入、终端输出,那么当命令需要获取输入的时候,它会去读取fd0,当要输出的时候,它会像fd1、fd2写入,改变这些描述指向的行为叫做重定向
02_判断命令
shell中提供了test、[、[[三种判断符号,可用于:
- 整数测试
- 字符串测试
- 文件测试
语法:
- test condition
- [ condition ]
- [[ condition ]]
注意:
- 中括号前后要有空格符
- [ 和 test是命令,只能使用自己支持的标志位,<、>、=只能用来比较字符串
- 中括号内的变量,最好都是用引号括起来
- [[ 更丰富,在整型比较中支持<、>、=,在字符串比较中支持=~正则
02_分支语句
02_循环
02_函数
语法一:
function(){ echo "abc" ;}
语法二:
function funcName(){ echo "abc"; }
注意:
- shell自上而下执行,函数必须在使用前定义
- 函数获取变量和shell script类似,1 $2...获取
- 函数内return仅仅代表函数执行状态,不代表函数执行结果
- 返回结果一般使用echo、printf,在外面使用$()、``获取结果
- 如果没有return,函数状态上一条命令的执行状态,存储在$?中
02_模块化
模块化的原理是在当前shell内执行函数文件,方式:
source [函数库的路径]
02_常用命令
03.执行过程和原理
03_执行
1.shell脚本一般以.sh结尾,也可以没有,这是一个约定;第一行需要指定用声什么命令解释器来执行
#! /bin/bash
#! /usr/bin/env/ bash
2.启动方式
# 文件名运行
./filename.sh
# 解释器运行
bash ./filename.sh
# source 运行
source ./filename.sh
03_执行过程
1.字符解析
- 识别换行符、分号(;)做行的分割
- 识别命令连接符(|| && 管道)做命令的分割
- 识别空格、tab符,做命令和参数的分割
2.shell 展开,例如 {1, 3} 解析为 1 2 3
3.重定向,将stdin、stdout、stderr的文件描述符进行指向变更
4.执行命令
- builtin 直接执行
- 非builtin使用$PATH查找,然后启动子进程执行
5.收集状态并返回
03_shell展开
1.大括号展开 (Brace Expansion){...}
一般由三部分构成,前缀、一对大括号、后缀,大括号内可以是序列表达式{x..y[..incr]}
# 字符串序列
a{b,c,d}e => abe ace ade
#表达式序列,(数字可以使用incr调整增量,字符不行)
{1..5} => 1 2 3 4 5
{1..5..2} => 1 3 5
{a..e} => a b c d e
2.波浪号(Tide Expansion)~
# 当前用户主目录
- => $HOME
~/foo => $HOME/foo
#指定用户的主目录
~fred/foo => 用户fred的$HOME/foo
#当前工作目录
~-+/foo => $PWD/foo
#上一个工作目录
~-/foo => ${$OLDPWD- '~-'}/foo
3.参数展开(Shell Parameter Expansion)${}
1.间接参数扩展${!parameter}, 其中引用的参数并不是parameter而是parameter的实际的值
2.参数长度${#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、
4.命令替换
在子进程中执行命令,并用得到的结果替换包裹的内容,形式上有两种:${...} 或
...
#!/bin/bash
echo ${whoimi}
foo(){
echo "asdasd"
}
a=`foo`
5.数学计算(Arithmetic Expansion)$((...))
使用$(())包裹数学运算表达式,得到结果并替换
#! /bin/bash
echo $((1+2)) # 3
6.文件名展开(Filename Expansion)*?[...] 外壳文件名模式匹配
当有单词没有被引号包裹,且其中出现了 '*', '?' , and , '[' 字符,则shell会去按照正则匹配的方式查找文件名进行替换,如果没找到则保持不变。
#!/bin/bash
$ echo D*
#输出当前目录下所有以D字母开头的目录、文件
04.调试和前端集成
04_调试
1.普通log,使用echo、printf
2.使用set命令
3.vscode debug插件
| set配置 | 作用 | 补充 |
|---|---|---|
| -u | 遇到不存在的变量就会报错,并停止执行 | -o nounset |
| -x | 运行结果之前,先输出执行的哪一行命令 | -o xtrace |
| -e | 只要发生错误,就终止执行 | -o errexit |
| -o pipefail | 管道链接的,只要一个子命令失败,整个管道命令就失败,脚本就会终止执行。 |
04_VSCode配置
1.shellman:代码提示的自动补全
2.shellcheck:代码语法校验
3.shell-format:代码格式化
3.Bash Debug:支持单步调试
- 安装vcode插件
- 编写launch.json文件
- 升级bash到4x以上版本
04_前端集成
1.node中通过exec、sqawn调用shell命令
2.shell脚本中调用node命令
3.借助zx等库进行javascript、shell script的融合
- 借助shell完成系统操作,文件io、内存、磁盘系统状态查
- 借助nodejs完成应用层能力,网络io、计算等