Shell 命令执行远不止引号处理,它涵盖了从命令解析、查找、执行到进程管理的完整生命周期。本文系统梳理 Shell 命令执行的核心知识点,配合图表与实例,构建完整的认知框架。
一、命令解析与查找优先级
1.1 命令解析的完整流程
当用户在 Shell 中输入一行命令时,Bash 按以下严格顺序解析:
#mermaid-svg-BNQvRDGIZl5gupRk{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BNQvRDGIZl5gupRk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BNQvRDGIZl5gupRk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BNQvRDGIZl5gupRk .error-icon{fill:#552222;}#mermaid-svg-BNQvRDGIZl5gupRk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BNQvRDGIZl5gupRk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BNQvRDGIZl5gupRk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BNQvRDGIZl5gupRk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BNQvRDGIZl5gupRk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BNQvRDGIZl5gupRk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BNQvRDGIZl5gupRk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BNQvRDGIZl5gupRk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BNQvRDGIZl5gupRk .marker.cross{stroke:#333333;}#mermaid-svg-BNQvRDGIZl5gupRk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BNQvRDGIZl5gupRk p{margin:0;}#mermaid-svg-BNQvRDGIZl5gupRk .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-BNQvRDGIZl5gupRk .cluster-label text{fill:#333;}#mermaid-svg-BNQvRDGIZl5gupRk .cluster-label span{color:#333;}#mermaid-svg-BNQvRDGIZl5gupRk .cluster-label span p{background-color:transparent;}#mermaid-svg-BNQvRDGIZl5gupRk .label text,#mermaid-svg-BNQvRDGIZl5gupRk span{fill:#333;color:#333;}#mermaid-svg-BNQvRDGIZl5gupRk .node rect,#mermaid-svg-BNQvRDGIZl5gupRk .node circle,#mermaid-svg-BNQvRDGIZl5gupRk .node ellipse,#mermaid-svg-BNQvRDGIZl5gupRk .node polygon,#mermaid-svg-BNQvRDGIZl5gupRk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-BNQvRDGIZl5gupRk .rough-node .label text,#mermaid-svg-BNQvRDGIZl5gupRk .node .label text,#mermaid-svg-BNQvRDGIZl5gupRk .image-shape .label,#mermaid-svg-BNQvRDGIZl5gupRk .icon-shape .label{text-anchor:middle;}#mermaid-svg-BNQvRDGIZl5gupRk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-BNQvRDGIZl5gupRk .rough-node .label,#mermaid-svg-BNQvRDGIZl5gupRk .node .label,#mermaid-svg-BNQvRDGIZl5gupRk .image-shape .label,#mermaid-svg-BNQvRDGIZl5gupRk .icon-shape .label{text-align:center;}#mermaid-svg-BNQvRDGIZl5gupRk .node.clickable{cursor:pointer;}#mermaid-svg-BNQvRDGIZl5gupRk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-BNQvRDGIZl5gupRk .arrowheadPath{fill:#333333;}#mermaid-svg-BNQvRDGIZl5gupRk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-BNQvRDGIZl5gupRk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-BNQvRDGIZl5gupRk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BNQvRDGIZl5gupRk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-BNQvRDGIZl5gupRk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BNQvRDGIZl5gupRk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-BNQvRDGIZl5gupRk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-BNQvRDGIZl5gupRk .cluster text{fill:#333;}#mermaid-svg-BNQvRDGIZl5gupRk .cluster span{color:#333;}#mermaid-svg-BNQvRDGIZl5gupRk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-BNQvRDGIZl5gupRk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-BNQvRDGIZl5gupRk rect.text{fill:none;stroke-width:0;}#mermaid-svg-BNQvRDGIZl5gupRk .icon-shape,#mermaid-svg-BNQvRDGIZl5gupRk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BNQvRDGIZl5gupRk .icon-shape p,#mermaid-svg-BNQvRDGIZl5gupRk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-BNQvRDGIZl5gupRk .icon-shape .label rect,#mermaid-svg-BNQvRDGIZl5gupRk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BNQvRDGIZl5gupRk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-BNQvRDGIZl5gupRk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-BNQvRDGIZl5gupRk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
否
是
否
是
否
是
否
是
否
是
否
是
否
用户输入命令行
历史扩展
感叹号加历史
大括号扩展
花括号逗号列表
波浪号扩展
波浪号加用户名
参数/变量扩展
美元符号加变量名
命令替换
美元符号加括号或反引号
算术扩展
双括号算术表达式
词法切分
按 IFS 拆分
路径名扩展
通配符星号和问号
引号移除
命令名含斜杠?
直接执行指定路径
命令查找优先级
别名 alias?
执行别名
保留关键字?
作为关键字处理
函数 function?
执行函数
内建命令 builtin?
执行内建命令
hash 表中有?
执行 hash 缓存的命令
按 PATH 搜索
找到可执行文件?
执行外部命令
command_not_found_handle
函数存在?
执行该函数
报错: 命令未找到
1.2 命令查找优先级详解
| 优先级 | 类型 | 说明 | 强制调用方式 |
|---|---|---|---|
| 1 | 别名(alias) | 交互式 Shell 或 expand_aliases 开启时优先匹配 |
\command 绕过别名 |
| 2 | 保留关键字 | if、for、while、case 等控制结构关键字 |
无法被覆盖 |
| 3 | 函数(function) | 用户定义的 Shell 函数 | command func 绕过函数 |
| 4 | 内建命令(builtin) | Shell 内置实现,如 cd、echo、pwd |
builtin cmd 强制使用内建 |
| 5 | hash 缓存 | Shell 缓存的外部命令路径,加速查找 | hash -r 清除缓存 |
| 6 | PATH 搜索 | 按 PATH 环境变量顺序查找可执行文件 | 使用完整路径绕过所有优先级 |
| 7 | 错误处理 | 未找到时调用 command_not_found_handle 或报错 |
--- |
验证实验:
bash
# 1. 验证别名优先级最高
alias test="ls -l"
function test() { echo "function"; }
test # 输出:ls -l 的结果(别名优先)
# 2. 删除别名后函数生效
unalias test
test # 输出:function
# 3. 使用 builtin 强制内建命令
builtin echo "hello" # 强制使用内建 echo
# 4. 使用 command 绕过别名和函数
command test # 执行 /usr/bin/test(外部命令)
# 5. type -a 查看所有匹配
type -a echo
# echo is a shell builtin
# echo is /bin/echo
二、命令类型与执行机制
2.1 内建命令 vs 外部命令
| 特性 | 内建命令(Builtin) | 外部命令(External) |
|---|---|---|
| 实现位置 | Shell 自身代码中 | 磁盘上的可执行文件 |
| 执行方式 | 直接调用,无 fork | fork() + execve() |
| 性能 | 快,无进程创建开销 | 慢,需创建新进程 |
| 环境依赖 | 直接修改当前 Shell 环境 | 在子进程中运行,无法直接修改父 Shell |
| 典型命令 | cd、echo、pwd、export、source |
ls、cat、grep、awk |
| 查看方式 | type cd → "cd is a shell builtin" |
type ls → "ls is /bin/ls" |
关键区别示例:
bash
# cd 必须是内建命令,因为外部命令无法修改父 Shell 的工作目录
cd /tmp # 当前 Shell 目录改变
(/bin/cd /tmp) # 子进程目录改变,父 Shell 不变
# source 必须是内建命令,用于在当前 Shell 执行脚本
source env.sh # 当前 Shell 导入环境变量
./env.sh # 子 Shell 执行,环境变量不保留
2.2 命令替换的两种形式
bash
# 反引号形式(旧式,不推荐)
output=`command`
# $() 形式(现代,推荐)
output=$(command)
# 关键区别:嵌套能力
echo $(echo $(echo nested)) # 正常嵌套
echo `echo \`echo nested\`` # 反引号需要转义,易出错
# 另一个区别:$() 内可用双引号保护
file="my file.txt"
cat "$(cat <<< "$file")" # $() 内可安全使用引号
三、进程管理与执行环境
3.1 前台、后台与作业控制
bash
# 前台执行(默认)
command
# 后台执行
command &
# 暂停前台进程
Ctrl+Z
# 作业控制命令
jobs # 查看作业列表
fg %1 # 将作业 1 调到前台
bg %1 # 将作业 1 放到后台继续
kill %1 # 终止作业 1
wait # 等待所有后台作业完成
3.2 子 Shell 与当前 Shell 分组
bash
# 子 Shell 分组 ():在子进程中执行,变量修改不影响父 Shell
(
cd /tmp
echo "In subshell: $PWD" # /tmp
)
echo "In parent: $PWD" # 原目录不变
# 当前 Shell 分组 {}:在当前进程中执行
{
cd /tmp
echo "In current shell: $PWD" # /tmp
}
echo "After: $PWD" # /tmp(已改变!)
# 注意 {} 语法要求空格和分号
{ echo a; echo b; } # 正确
{ echo a; echo b } # 错误:缺少最后的分号
3.3 进程替换(Process Substitution)
bash
# <(command):将命令输出作为只读文件
diff <(sort file1) <(sort file2)
# >(command):将文件作为命令的输入
tee >(wc -l > line_count.txt) >(wc -c > byte_count.txt) < input.txt
# 底层机制:创建命名管道 /dev/fd/N
echo <(date) # 输出:/dev/fd/63
四、输入输出重定向体系
4.1 标准流与文件描述符
#mermaid-svg-oThqgR1Yb6uNmDr6{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-oThqgR1Yb6uNmDr6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-oThqgR1Yb6uNmDr6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-oThqgR1Yb6uNmDr6 .error-icon{fill:#552222;}#mermaid-svg-oThqgR1Yb6uNmDr6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-oThqgR1Yb6uNmDr6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-oThqgR1Yb6uNmDr6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-oThqgR1Yb6uNmDr6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-oThqgR1Yb6uNmDr6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-oThqgR1Yb6uNmDr6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-oThqgR1Yb6uNmDr6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-oThqgR1Yb6uNmDr6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-oThqgR1Yb6uNmDr6 .marker.cross{stroke:#333333;}#mermaid-svg-oThqgR1Yb6uNmDr6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-oThqgR1Yb6uNmDr6 p{margin:0;}#mermaid-svg-oThqgR1Yb6uNmDr6 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-oThqgR1Yb6uNmDr6 .cluster-label text{fill:#333;}#mermaid-svg-oThqgR1Yb6uNmDr6 .cluster-label span{color:#333;}#mermaid-svg-oThqgR1Yb6uNmDr6 .cluster-label span p{background-color:transparent;}#mermaid-svg-oThqgR1Yb6uNmDr6 .label text,#mermaid-svg-oThqgR1Yb6uNmDr6 span{fill:#333;color:#333;}#mermaid-svg-oThqgR1Yb6uNmDr6 .node rect,#mermaid-svg-oThqgR1Yb6uNmDr6 .node circle,#mermaid-svg-oThqgR1Yb6uNmDr6 .node ellipse,#mermaid-svg-oThqgR1Yb6uNmDr6 .node polygon,#mermaid-svg-oThqgR1Yb6uNmDr6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-oThqgR1Yb6uNmDr6 .rough-node .label text,#mermaid-svg-oThqgR1Yb6uNmDr6 .node .label text,#mermaid-svg-oThqgR1Yb6uNmDr6 .image-shape .label,#mermaid-svg-oThqgR1Yb6uNmDr6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-oThqgR1Yb6uNmDr6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-oThqgR1Yb6uNmDr6 .rough-node .label,#mermaid-svg-oThqgR1Yb6uNmDr6 .node .label,#mermaid-svg-oThqgR1Yb6uNmDr6 .image-shape .label,#mermaid-svg-oThqgR1Yb6uNmDr6 .icon-shape .label{text-align:center;}#mermaid-svg-oThqgR1Yb6uNmDr6 .node.clickable{cursor:pointer;}#mermaid-svg-oThqgR1Yb6uNmDr6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-oThqgR1Yb6uNmDr6 .arrowheadPath{fill:#333333;}#mermaid-svg-oThqgR1Yb6uNmDr6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-oThqgR1Yb6uNmDr6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-oThqgR1Yb6uNmDr6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oThqgR1Yb6uNmDr6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-oThqgR1Yb6uNmDr6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oThqgR1Yb6uNmDr6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-oThqgR1Yb6uNmDr6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-oThqgR1Yb6uNmDr6 .cluster text{fill:#333;}#mermaid-svg-oThqgR1Yb6uNmDr6 .cluster span{color:#333;}#mermaid-svg-oThqgR1Yb6uNmDr6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-oThqgR1Yb6uNmDr6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-oThqgR1Yb6uNmDr6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-oThqgR1Yb6uNmDr6 .icon-shape,#mermaid-svg-oThqgR1Yb6uNmDr6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oThqgR1Yb6uNmDr6 .icon-shape p,#mermaid-svg-oThqgR1Yb6uNmDr6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-oThqgR1Yb6uNmDr6 .icon-shape .label rect,#mermaid-svg-oThqgR1Yb6uNmDr6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oThqgR1Yb6uNmDr6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-oThqgR1Yb6uNmDr6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-oThqgR1Yb6uNmDr6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} stdout 1
stdin 0
stderr 2
进程
终端/文件
键盘/文件
终端/文件
4.2 重定向操作符大全
bash
# 输出重定向
> file # stdout 覆盖写入文件
>> file # stdout 追加写入文件
2> file # stderr 覆盖写入文件
2>> file # stderr 追加写入文件
&> file # stdout + stderr 同时重定向(bash 4+)
> file 2>&1 # 同上,经典写法
# 输入重定向
< file # 从文件读取 stdin
<< EOF # Here Document,多行输入
<<< "string" # Here String,字符串作为 stdin
# 文件描述符操作
3<> file # 打开文件作为 fd 3(读写)
exec 3> file # 将 fd 3 绑定到文件
exec 3<&- # 关闭 fd 3
# 示例:分离 stdout 和 stderr
command > out.log 2> err.log
# 示例:丢弃所有输出
command > /dev/null 2>&1
command &> /dev/null
4.3 Here Document vs Here String
| 特性 | Here Document << |
Here String <<< |
|---|---|---|
| 语法 | << EOF ... EOF |
<<< "string" |
| 输入源 | 多行文本块 | 单行字符串 |
| 变量扩展 | 默认扩展,用 << 'EOF' 禁用 |
执行扩展 |
| 用途 | 多行输入,如 SQL、配置文件 | 单行快速输入 |
| read 命令 | 适合读取多行 | 适合读取单行 |
bash
# Here Document
cat << EOF
Hello $USER
Today is $(date)
EOF
# 禁止变量扩展
cat << 'EOF'
Hello $USER # 输出字面 $USER
EOF
# Here String
read a b c <<< "1 2 3"
echo "$a, $b, $c" # 1, 2, 3
# 与 IFS 结合解析 /etc/passwd
IFS=: read user pw uid gid name home shell <<< "root:x:0:0:root:/root:/bin/bash"
五、变量与环境管理
5.1 变量类型与声明
bash
# 局部变量(当前 Shell)
local_var="value"
# 环境变量(导出到子进程)
export PATH="/usr/local/bin:$PATH"
declare -x VAR="value"
# 只读变量
readonly CONST="fixed"
declare -r CONST="fixed"
# 整数变量
declare -i num=10
num="abc" # 自动转为 0
# 数组
arr=(a b c)
echo ${arr[0]} # 第一个元素
echo ${arr[@]} # 所有元素
echo ${#arr[@]} # 数组长度
# 关联数组(bash 4+)
declare -A dict
dict["key"]="value"
echo ${dict["key"]}
5.2 高级参数扩展
bash
var="hello world"
# 默认值
echo ${var:-default} # var 未定义或为空时返回 default
echo ${var:=default} # var 未定义或为空时赋值 default
echo ${var:+alternative} # var 有值时返回 alternative
echo ${var:?error_msg} # var 未定义或为空时报错退出
# 子串操作
echo ${var:0:5} # 截取前5个字符:hello
echo ${var#*l} # 删除最短匹配前缀:lo world
echo ${var##*l} # 删除最长匹配前缀:d
echo ${var%l*} # 删除最短匹配后缀:hello wor
echo ${var%%l*} # 删除最长匹配后缀:he
# 替换
echo ${var/world/earth} # 首次替换:hello earth
echo ${var//l/L} # 全局替换:heLLo worLd
echo ${var/#h/H} # 前缀匹配替换:Hello world
echo ${var/%d/D} # 后缀匹配替换:hello worlD
# 大小写转换
echo ${var^^} # 全大写
echo ${var,,} # 全小写
六、IFS 与词法切分
6.1 IFS 的作用机制
IFS(Internal Field Separator)是控制词法切分的特殊变量:
bash
# 默认值:空格、制表符、换行
echo "$IFS" | cat -A # 显示 ^I$ $(制表符 + 换行)
# 自定义 IFS 解析 CSV
IFS=, read -ra fields <<< "a,b,c"
echo "${fields[@]}" # a b c
# 临时修改 IFS(推荐在子 Shell 中)
(
IFS=":"
echo "$PATH" | while read -ra dirs; do
echo "${dirs[@]}"
done
)
# 外部 IFS 保持不变
6.2 IFS 与 read 命令的配合
bash
# 读取 /etc/passwd 的一行
while IFS=: read -r user pw uid gid name home shell; do
echo "User: $user, Shell: $shell"
done < /etc/passwd
# IFS= 防止去除前后空格
while IFS= read -r line; do
echo "$line" # 保留原始空格
done < file.txt
七、条件测试与退出状态
7.1 退出状态码
| 状态码 | 含义 |
|---|---|
| 0 | 成功 |
| 1 | 通用错误 |
| 2 | 误用 Shell 命令 |
| 126 | 命令不可执行(权限问题) |
| 127 | 命令未找到 |
| 128+N | 被信号 N 终止 |
| 255 | 退出状态码超出范围 |
7.2 测试命令对比
bash
# 经典 test / [ ]
[ -f file ] && [ -r file ]
[ "$a" = "$b" ]
[ "$num" -eq 10 ]
# 现代 [[ ]](bash 特有,更安全)
[[ $str == pattern* ]] # 模式匹配
[[ $str =~ ^regex$ ]] # 正则表达式
[[ -f file && -r file ]] # 无需空格分隔
[[ $a > $b ]] # 字符串比较(不依赖 locale)
# 算术比较 (( ))
if (( a > b )); then
echo "a is greater"
fi
# 命令退出状态直接判断
if grep -q "pattern" file; then
echo "found"
fi
八、控制结构
8.1 条件分支
bash
# if 语句
if condition; then
commands
elif condition; then
commands
else
commands
fi
# case 语句(模式匹配)
case $var in
start|START)
echo "Starting..."
;;
stop)
echo "Stopping..."
;;
*)
echo "Unknown command"
;;
esac
# 三元运算风格
[[ -f file ]] && echo "exists" || echo "not found"
8.2 循环结构
bash
# for 循环
for i in 1 2 3; do
echo $i
done
for i in {1..10}; do # 序列扩展
echo $i
done
for ((i=0; i<10; i++)); do # C 风格
echo $i
done
for file in *.txt; do # Globbing
echo "$file"
done
# while 循环
while read -r line; do
echo "$line"
done < file.txt
# until 循环
until ping -c1 host; do
echo "Retrying..."
sleep 1
done
九、函数与模块化
9.1 函数定义与调用
bash
# 经典写法
myfunc() {
local var="local" # 局部变量
echo "param 1: $1"
return 0 # 返回状态码(0-255)
}
# 现代写法
function myfunc {
...
}
# 返回值处理
myfunc arg1 arg2
echo $? # 获取返回状态
# 返回字符串(通过 stdout 捕获)
get_value() {
echo "result"
}
result=$(get_value)
9.2 脚本模块化
bash
# source / . 导入其他脚本(在当前 Shell 执行)
source /path/to/lib.sh
. /path/to/lib.sh
# 检查脚本是否被直接执行
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi
十、信号与陷阱(Trap)
bash
# 捕获信号
trap 'echo "Caught SIGINT"; cleanup; exit 1' INT
trap 'echo "Exiting..."' EXIT
trap '' HUP # 忽略 SIGHUP
# 常用信号
# SIGHUP(1) SIGINT(2) SIGQUIT(3) SIGKILL(9) SIGTERM(15)
# 临时禁用中断
trap '' INT
# 执行关键操作...
trap - INT # 恢复默认处理
# 函数内局部陷阱
cleanup() {
trap - EXIT # 清除陷阱防止递归
rm -f "$tmpfile"
}
trap cleanup EXIT
十一、Shell 选项与严格模式
11.1 核心选项
| 选项 | 长形式 | 作用 |
|---|---|---|
set -e |
errexit |
命令失败时立即退出 |
set -u |
nounset |
使用未定义变量时报错 |
set -x |
xtrace |
打印每条执行的命令(调试) |
set -o pipefail |
--- | 管道中任一命令失败则整体失败 |
11.2 严格模式模板
bash
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
# 脚本主体
各选项的作用:
set -e:遇到非零退出状态的命令立即终止脚本,实现"快速失败"set -u:引用未定义变量时报错,防止rm -rf /$UNDEFINED_VAR类灾难set -o pipefail:管道中任意命令失败,整个管道返回非零状态,防止curl | sh类管道隐藏错误IFS=$'\n\t':限制词法切分只在换行和制表符上发生,避免空格导致的意外拆分
11.3 调试技巧
bash
#!/bin/bash -x # 脚本级调试
bash -x script.sh # 外部调试
# 条件调试
[[ ${DEBUG:-0} == 1 ]] && set -x
# 自定义调试输出
export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
十二、高级特性
12.1 协程(Coprocess,bash 4+)
bash
# 创建双向管道通信的协程
coproc bc_cmd { bc -l; }
# 向协程发送输入
echo "scale=10; 4*a(1)" >&${bc_cmd[1]}
# 从协程读取输出
read result <&${bc_cmd[0]}
echo $result # 3.1415926532
# 协程 PID
echo ${bc_cmd_PID}
12.2 算术运算的多种方式
| 方式 | 语法 | 特点 |
|---|---|---|
expr |
expr $a + $b |
外部命令,需转义 *,已过时 |
let |
let a=b+c |
内建命令,变量无需 $ |
$(( )) |
result=$((a+b)) |
算术扩展,现代推荐 |
(( )) |
((a++)) |
算术求值,可作条件 |
bc |
`echo "1.5+2.5" | bc` |
bash
# 现代推荐写法
a=$((3 + 4))
((a > 10)) && echo "big"
# C 风格 for 循环
for ((i=0; i<10; i++)); do
echo $i
done
十三、安全编码实践
13.1 命令注入防护
bash
# 危险:用户输入未加引号
user_input="; rm -rf /"
eval echo $user_input # 灾难!
# 安全:始终对变量使用引号
eval echo "$user_input" # 输出字面字符串
# 更安全的做法:避免 eval
printf '%s\n' "$user_input"
13.2 路径安全
bash
# 危险
rm -rf $dir/* # dir 为空时删除根目录!
# 安全
rm -rf "${dir:?}"/* # dir 未定义时报错
# 使用 mktemp 创建安全临时文件
tmpfile=$(mktemp /tmp/XXXXXX)
trap 'rm -f "$tmpfile"' EXIT
13.3 安全脚本模板
bash
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
# 参数校验
: "${1:?Usage: $0 <directory>}"
dir="$1"
# 路径规范化
dir=$(cd "$dir" && pwd)
# 安全操作
find "$dir" -type f -name "*.log" -print0 | while IFS= read -r -d '' file; do
process "$file"
done
十四、Shell 初始化文件体系
#mermaid-svg-fp9OhlYHsLMV4CZC{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-fp9OhlYHsLMV4CZC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-fp9OhlYHsLMV4CZC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-fp9OhlYHsLMV4CZC .error-icon{fill:#552222;}#mermaid-svg-fp9OhlYHsLMV4CZC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fp9OhlYHsLMV4CZC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-fp9OhlYHsLMV4CZC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fp9OhlYHsLMV4CZC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fp9OhlYHsLMV4CZC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-fp9OhlYHsLMV4CZC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fp9OhlYHsLMV4CZC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fp9OhlYHsLMV4CZC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fp9OhlYHsLMV4CZC .marker.cross{stroke:#333333;}#mermaid-svg-fp9OhlYHsLMV4CZC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fp9OhlYHsLMV4CZC p{margin:0;}#mermaid-svg-fp9OhlYHsLMV4CZC .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-fp9OhlYHsLMV4CZC .cluster-label text{fill:#333;}#mermaid-svg-fp9OhlYHsLMV4CZC .cluster-label span{color:#333;}#mermaid-svg-fp9OhlYHsLMV4CZC .cluster-label span p{background-color:transparent;}#mermaid-svg-fp9OhlYHsLMV4CZC .label text,#mermaid-svg-fp9OhlYHsLMV4CZC span{fill:#333;color:#333;}#mermaid-svg-fp9OhlYHsLMV4CZC .node rect,#mermaid-svg-fp9OhlYHsLMV4CZC .node circle,#mermaid-svg-fp9OhlYHsLMV4CZC .node ellipse,#mermaid-svg-fp9OhlYHsLMV4CZC .node polygon,#mermaid-svg-fp9OhlYHsLMV4CZC .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-fp9OhlYHsLMV4CZC .rough-node .label text,#mermaid-svg-fp9OhlYHsLMV4CZC .node .label text,#mermaid-svg-fp9OhlYHsLMV4CZC .image-shape .label,#mermaid-svg-fp9OhlYHsLMV4CZC .icon-shape .label{text-anchor:middle;}#mermaid-svg-fp9OhlYHsLMV4CZC .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-fp9OhlYHsLMV4CZC .rough-node .label,#mermaid-svg-fp9OhlYHsLMV4CZC .node .label,#mermaid-svg-fp9OhlYHsLMV4CZC .image-shape .label,#mermaid-svg-fp9OhlYHsLMV4CZC .icon-shape .label{text-align:center;}#mermaid-svg-fp9OhlYHsLMV4CZC .node.clickable{cursor:pointer;}#mermaid-svg-fp9OhlYHsLMV4CZC .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-fp9OhlYHsLMV4CZC .arrowheadPath{fill:#333333;}#mermaid-svg-fp9OhlYHsLMV4CZC .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-fp9OhlYHsLMV4CZC .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-fp9OhlYHsLMV4CZC .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fp9OhlYHsLMV4CZC .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-fp9OhlYHsLMV4CZC .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fp9OhlYHsLMV4CZC .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-fp9OhlYHsLMV4CZC .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-fp9OhlYHsLMV4CZC .cluster text{fill:#333;}#mermaid-svg-fp9OhlYHsLMV4CZC .cluster span{color:#333;}#mermaid-svg-fp9OhlYHsLMV4CZC div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-fp9OhlYHsLMV4CZC .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-fp9OhlYHsLMV4CZC rect.text{fill:none;stroke-width:0;}#mermaid-svg-fp9OhlYHsLMV4CZC .icon-shape,#mermaid-svg-fp9OhlYHsLMV4CZC .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fp9OhlYHsLMV4CZC .icon-shape p,#mermaid-svg-fp9OhlYHsLMV4CZC .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-fp9OhlYHsLMV4CZC .icon-shape .label rect,#mermaid-svg-fp9OhlYHsLMV4CZC .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fp9OhlYHsLMV4CZC .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-fp9OhlYHsLMV4CZC .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-fp9OhlYHsLMV4CZC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
否
Shell 启动
Login Shell?
读取 /etc/profile
Interactive?
读取 ~/.bash_profile
或 ~/.bash_login
或 ~/.profile
通常包含
source ~/.bashrc
读取 ~/.bashrc
读取 $BASH_ENV
指定的文件
读取 /etc/bash.bashrc
| 文件 | 执行时机 | 典型用途 |
|---|---|---|
/etc/profile |
登录 Shell | 系统级环境变量 |
~/.bash_profile |
登录 Shell | 用户级环境变量、PATH |
~/.bash_login |
登录 Shell(备选) | 兼容其他 Shell |
~/.profile |
登录 Shell(备选) | 通用 Shell 配置 |
~/.bashrc |
交互式非登录 Shell | 别名、函数、提示符 |
/etc/bash.bashrc |
交互式非登录 Shell | 系统级别名和函数 |
~/.bash_logout |
登录 Shell 退出 | 清理操作 |
总结
Shell 命令执行的知识体系可从以下维度把握:
| 维度 | 核心要点 |
|---|---|
| 解析阶段 | 大括号、波浪号、变量、命令替换、算术、词法切分、Globbing、引号移除 |
| 查找优先级 | alias → 关键字 → function → builtin → hash → PATH → error |
| 执行机制 | 内建命令直接执行,外部命令 fork+exec,函数在当前 Shell 执行 |
| 进程管理 | 前台/后台、作业控制、子 Shell、进程替换、协程 |
| IO 体系 | 重定向、管道、Here Document/Here String、文件描述符 |
| 变量管理 | 局部/环境/只读/整数/数组/关联数组,高级参数扩展 |
| 控制结构 | 条件测试、if/case、for/while/until、函数 |
| 安全编码 | 严格模式(set -euo pipefail)、引号保护、输入验证、路径安全 |
掌握这些知识点,能够编写出健壮、高效且安全的 Shell 脚本,应对从日常自动化到复杂系统管理的各类场景。