写在前面
学习shell的价值:
- Linux 服务器的基本操作和管理
- 前端 Node.js 服务的进程管理、问题排查、资源监控等运维操作
- 使用 shell 编写 TCE、SCM、Docker 脚本,完成服务编译和部署
Shell基础概念
Shell是一种命令行解释器,它是用户与操作系统内核之间的接口。它允许用户输入命令并将其传递给操作系统内核执行。Shell还提供了许多实用程序和工具,例如文件管理、文本编辑、进程管理等。在Unix和Linux系统中,常见的Shell包括Bash、Zsh、Ksh等。Windows系统中的Shell是命令提示符(Command Prompt)和PowerShell。
发展
构成
Shell由命令解释器(解释器)和实用程序(编程语言)组成。
命令解释器是Shell的核心组件,它负责解释和执行用户输入的命令。常见的命令解释器包括Bash、Zsh、Ksh等。实用程序是Shell提供的一系列工具和程序,用于完成各种任务,例如文件管理、文本编辑、进程管理等。常见的实用程序包括ls、cd、cp、mv、rm等。
命令和语法
Shell命令是一种在Shell中执行的指令,用于完成各种任务,例如文件管理、文本编辑、进程管理等。Shell命令通常由命令名和一些选项和参数组成。Shell语法是一种用于编写Shell脚本的语言,它由一系列Shell命令和控制结构组成。
变量
| 类型 | 作用域 | 声明方式 | 规范 |
|---|---|---|---|
| 自定义变量 | 当前shell | = | 字符串、整型、浮点型、日期型 |
| 环境变量 | 当前shell及其子shell | export、declare -x | |
| 系统环境变量 | 所有shell | 启动加载 |
自定义变量
#!/bin/bash
# 变量名=变量值
test=123
# 将命令结果赋值给变量
_ls=ls
# 将命令结果赋值给变量
output=$(ls /)
# 声明变量为整型(两种)
let total=size*count
declare -i total=size*count
# 导出环境变量(两种)
export total
declare -x total
注意:
- 等号左右不能由空格
- 只有
total=size*count默认为字符串,不会进行运算。
系统环境变量
| 变量名 | 含义 | 常见操作 |
|---|---|---|
| $0 | 当前shell名称/脚本名称 | $1、$2等可以获取到传入参数 |
| $# | 传入脚本的参数数量 | if [ $# -gt 1 ] |
| $* | 传入脚本的所有参数 | |
| $? | 上条命令执行的状态码 | if [ $? -eq 0 ]; |
| $PS1 | 命令提示符 | export PS1="\u@\h \w> " |
| $HOME | 用户主文件夹 | cd ~ |
| $PATH | 全局命令的搜索路径 | PATH=$PATH:[新增路径] |
配置文件加载
在Shell中,可以使用source命令或.命令来加载配置文件。这两个命令的作用是相同的,都是将指定的文件中的命令和变量加载到当前Shell会话中。
source ~./bashrc
. ~./bashrc
运算符和引用
| 类型 | 符号 | 作用 | 用法 |
|---|---|---|---|
| 算数运算符 | + - * / % | & | 常规运算 | |
| 逻辑运算符 | || && ! | ||
| 比较运算符 | == != < > | ||
| 引号 | 双引号 "" | 部分引用, 仅仅 $ ` \ 保留作用 | foo="${a}123" |
| 引号 | 单引号 '' | 完全引用,原样输出 | foo=‘foo$a’ |
| 引号 | 反引号 `` | 执行命令 | foo=`ls -a` |
| 圆括号 | (()) | 算数运算 | foo=$((1+2)) |
| 圆括号 | () | 执行命令 | $(ls -a) |
| 命令连接 | || | cmd1执行完且返回码非0,则继续执行cmd2 | cmd1 || cmd2 |
| 命令连接 | && | cmd1执行完且返回码为0,则继续执行cmd2 | cmd1 && cmd2 |
| 命令连接 | ; | cmd1、cmd2串行执行 | cmd1 ; cmd2 |
| 后台运行 | & | 让命令在后台运行,可与nohup一起使用 | cmd & |
# 在运行node的时候不影响shell命令(shell关闭也会停止)
node xxxx &
# shell关闭不停止
nohup node xxxx &
管道
在Shell中,管道符(|)用于将一个命令的输出作为另一个命令的输入。这使得可以将多个命令组合在一起,以便在单个命令行中执行多个操作,作用是将前一个命令的结果传递给后一个命令。
- 语法为:cmd1 | cmd2,
- 要求:
管道右侧的命令必须能接受标准输入才行,比如 grep 命令,ls、mv等不能直接使用,可以使用 xargs预处理 - 注意:
管道命令仅仅处理stdout,对于stderr会予以忽略,可以使用set -o pipefail设置shell遇到管道错误退出
find . -maxdepth 1 -name "*.sh" | xargs ls -l
重定向
在Shell中,重定向是指将命令的输入或输出从默认的位置重定向到其他位置。重定向可以使用特殊字符<和>来实现。
输出重定向符号:
- >:覆盖写入文件
- >>:追加写入文件
- 2>:错误输出写入文件
- &>:正确和错误输出统一写入到文件中
输入重定向符号:
- <:将文件的内容作为命令的输入
- <<:将文本块作为命令的输入
判断命令
shell中提供了test、[ 、[[三种判断符号,可用于:
- 整数测试
- 字符串测试
- 文件测试
语法:
- test condition
- [ condition ]
- [[condition]]
# 整数测试
test $n1 -eq $n2
test $n1 -lt $n2
test $n1 -gt $n2
# 字符串测试
test -z $str
test -n $str
# 文件测试
test -e /demo && echo "success"
test -f /usr/bin/npm && echo "success"
注意:
- 不能hello world = “hello",会报错,该使用"hello world" = “hello"。
整数测试中,比较操作符包括-eq(等于)、-ne(不等于)、-lt(小于)、-le(小于等于)、-gt(大于)和-ge(大于等于)。字符串测试中,-z选项用于检查字符串长度是否为零。如果字符串长度为零,则返回true;否则返回false。-n选项用于检查字符串长度是否不为零。如果字符串长度不为零,则返回true;否则返回false。- 在
文件测试中,test -e用于检查/demo是否存在,test -f用于检查文件是否存在并且是一个常规文件。- 中括号前后要有
空格符。- [和test是命令,
只能使用自己支持的标志位,<、>、=只能用来比较字符串。- 中括号内的变量,最好都是用引号括起来。
- [[更丰富,在
整型比较中支持<、>、=,在字符串比较中支持=~正则。
分支语句
在Shell脚本中,分支语句用于根据条件执行不同的命令。
语法1:
if condition ; then
程序段
elif condition ; then
程序段
esle
程序段
fi
语法2:
case $变量 in:
"第一个变量内容")
程序段
;;
"第一个变量内容")
程序段
;;
*)
程序段
;;
esac
循环语句
在Shell脚本中,循环语句用于重复执行一组命令。循环语句有:
- while循环
while condition ;
do
程序段;
done
- until循环
until condition ;
do
程序段;
done
- for循环
for var in [words...];
do
程序段;
done
函数
在Shell中,可以编写函数封装某一些功能。
语法1:
funcName(){
echo "abc";
}
语法2:
function funcName() {
echo "abc";
}
注意:
- shell
自上而下执行,函数必须在使用前定义- 函数获取变量和shell script类似,
$0代表函数名,后续参数通过2...获取- 函数内return仅仅
表示函数执行状态,不代表函数执行结果返回结果一般使用echo、printf,在外面使用$()、``获取结果- 如果
没有return,函数状态是上一条命令的执行状态,存储在$?中
模块化
模块化的原理是在当前shell内执行函数,语法为
source [函数库路径]
常用命令
| 命令 | 使用 |
|---|---|
| grep | 查找错误日志:grep -n "ERROR" -A3 -B3 cloudfun.log;统计次数:grep -n "ERROR" -c cloudfun.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是一种命令行解释器,它允许用户与操作系统进行交互。当用户在Shell中输入命令时,Shell会解释并执行这些命令。
执行
- shell脚本一般以.sh结尾,也可以没有,这是一个约定;第一行需要指定用声明用什么命令解释器来执行。
# 两种方式
#! /bin/bash
#! /usr/bin/env bash
- 启动方式
# 文件名运行
./test.sh
# 解释器执行
bash ./test.sh
# source执行
source ./test.sh
执行过程
- 字符解析
- 识别换行符、分号(;)做行的分割
- 识别命令连接符(|| && 管道) 做命令的分割
- 识别空格、tab符,做命令和参数的分割
- shell 展开,例如 {1..3} 解析为 1 2 3
- 重定向,将stdin、stdout、stderr的文件描述符进行指向变更
- 执行命令
- builtin 直接执行
- 非builtin使用$PATH查找,然后启动子进程执行
- 收集状态并返回
shell展开
- 大括号展开(Brace Expansion){...}
- 波浪号展开(Tilde Expansion) ~
- 参数展开(Shell Parameter Expansion)
- 命令替换(Command Substitution)
- 数学计算(Arithmetic Expansion) $((..))
- 文件名展开(Filename Expansion)* ? [..] 外壳文件名模式匹配
大括号展开
一般由三部分构成,前缀、一对大括号、后缀,大括号内可以是逗号分割的字符串序列,也可以是序列表达式 {x..y[..incr]}。
# 字符串序列
a {b c d} e => abe ace ade
# 表达式序列
{1 .. 5} => 1 2 3 4 5
{a .. e} => a b c d e
{1 .. 5 .. 2} => 1 3 5
波浪号展开
# 当前用户主目录
~ # HOME
~/test # HOME/test
# 指定用户的主目录
~test/foo # HOME/foo
# 当前工作目录
~+/foo # /PWD/foo
# 上一级工作目录
~-/foo # /foo
参数展开
- 间接参数扩展${!parameter},其中
引用的参数并不是parameter而是parameter的实际的值 - 参数长度${#parameter}
- 空参数处理
- ${parameter:-word} # 为空替换
- ${parameter:=word} # 为空替换,并将值赋给$parameter变量
- ${parameter:?word} # 为空报错
- ${parameter:+word} # 不为空替换
- 参数切片
- ${parameter:offset}
- ${parameter:offset:length}
- 参数部分删除
- ${parameter%word} # 最小限度从后面截取word
- ${parameter%%word} # 最大限度从后面截取word
- ${parameter#word} # 最小限度从前面截取word
- ${parameter##word} # 最大限度从前面截取word
命令替换
在子进程中执行命令,并用得到的结果替换包裹的内容,形式上有两种:$(...) 或 `...`。
echo $(ls /)
function fun() {
echo "hello"
}
a = `fun`
数学计算
使用$(())包裹数学运算表达式,得到结果并替换。
echo $((1+2)) # 3
文件名展开
当有单词没有被引号包裹,且其中出现了‘*’,‘?’,and ‘[’字符,则shell会去按照正则匹配的方式查找文件名进行替换,如果没找到则保持不变。
# 查找所有以f开头的文件或目录
echo f*
调试和前端集成
shell也经常用于前端的调试以及集成工具中。
调试
- 普通log,使用echo、printf
- 使用set命令
- vscode debug插件
| set配置 | 作用 | 补充 |
|---|---|---|
| -u | 遇到不存在的变量就会报错,并停止执行。 | -o nounset |
| -x | 运行结果之前,先输出执行的那一行命令。 | -o xtrace |
| -e | 只要发生错误,就终止执行 | -o errexit |
| -o | pipefail管道符链接的,只要一个子命令失败,整个管道命令就失败 |
前端集成
- node中通过exec、spawn调用shell命令
- shell脚本中调用node命令
- 借助zx等库进行javascript、shell script的融合
- 借助shell完成系统操作,文件io、内存、磁盘系统状态查询等
- 借助 nodejs 完成应用层能力, 网络io、计算等