Shell脚本的执行过程和原理
Shell脚本一般以.sh结尾,也可以没有,这是一个约定
在脚本第一行需要指定使用什么命令解释器来执行:
#! /bin/bash
or
#! /usr/bin/env bash
启动shell脚本的方式:
- 文件名运行 :
./filename.sh - 解释器运行 :
bash ./filename.sh - source运行:
source ./filename.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
#表达式序列, (数字可以使用incr调整增量,字母不行)
{1..5} => 1 2 3 4 5
{1..5..2} => 1 3 5
{a..e} => a b c d e
波浪号展开
# 当前用户主目录
- => $HOME
-/foo => $HOME/FOO
# 指定用户的主目录
-fred/foo => 用户fred的 $HOME/foo
# 当前工作目录
-+/foo => $PWD/foo
# 上一个工作目录
--/foo => $ ${SOLDPWD-'--'}/foo
参数展开
-
间接参数扩展 ${!parameter},其中引用的参数并不是parameter而是parameter的实际的值
parameter="var" var="hello" echo ${!parameter} # 输出hello在echo输出parameter时,会逐层展开,先去寻找parameter,发现其对应var,然后再去寻找var,最终找到hello
-
参数长度${#parameter}
par=cd echo ${#par} # 输出2 -
空参数处理 {parameter:-word} # 为空替换 {parameter:=word} # 为空替换,并将值赋值给{parameter:?word} # 为空报错 ${parameter:+word} # 不为空替换
a=1 echo ${a:-word} # 因为a有值,所以输出1 echo ${b:-word} # 因为b为空,所以输出word echo ${par:=word} # 因为par为空,所以会输出word,且赋值给par echo ${par:-hello} # 因为par已经为word,所以输出word echo ${par:+foo} # 因为par不为空,所以不为空替换输出foo -
参数切片 {parameter: offset: length}
-
参数部分删除 {parameter%word} # 最小限度从后面截取word {parameter%%word} # 最大限度从后面截取word ${parameter##word} # 最大限度从前面截取word
str=abcdefg # ##表示最大程度从前方匹配截取,配合*匹配abcd sp1=${str##*d} # %%表示最大程度从后方匹配截取,配合*匹配defg sp2=${str%%d*} echo $sp1 # 输出efg echo $sp2 # 输出abc
命令替换
在子进程中执行命令,并用得到的结果替换包裹的内容,形式上有两种:
$(...)或`...`
echo ${whoimi}
foo(){
echo "asdasd"
}
a=`foo`
数学计算
使用$(()) 包裹数学运算表达式,得到结果并替换
echo $((1+2)) # 3
文件名展开
当有单词没有被引号包裹,且其中出现了 '*', '?', and '[' 字符,则shell会求按照正则匹配的方式查找文件名进行替换,如果找到则保持不变。
$ echo D* # 输出当前目录下所有以D字母开头的目录、文件