Linux Shell 内建命令速查
更新时间:2026-08-28。本文是 Linux 命令速查主题第⑩族:Shell 内建命令,约 28 个。所谓内建(builtin,由 Shell 自身实现、不 fork 新进程的命令),它们执行时不会创建新进程,因此能改变当前 Shell 的状态——这是
cd、export这类命令必须是内建的根本原因:如果它们 fork 到子进程执行,改完子进程一退出,父 Shell 什么都没变。系统命令查 文件与目录,脚本写法见 Shell 编程主题。
一、环境变量
环境变量是进程启动时继承自父进程的一批键值对,子进程会复制一份。这决定了它的两个特性:往下传(子进程可见)容易,往上传(子进程改了影响父进程)不可能——所以要用 export 显式标记"这个变量要传给子进程"。
| 命令 | 作用 | 常用示例 |
|---|---|---|
export | 把变量标记为环境变量,使其对子进程可见;不加参数时列出当前所有环境变量 | export PATH=$PATH:/opt/bin;export -f func(导出函数);export -p(列出全部) |
env | 在指定环境下执行命令,或列出当前环境变量;常用于临时覆盖变量而不污染当前 Shell | env(列出);env -i ./app(清空环境运行,排查环境依赖);env LANG=C sort file(临时指定 locale) |
set | 设置 Shell 选项与位置参数;不带参数列出所有变量与函数,也用于开启严格模式 | set -e(命令失败即退出);set -u(未定义变量报错);set -x(打印执行的每条命令,调试神器);set -o pipefail(管道任一环节失败即失败) |
unset | 删除变量或函数定义,让变量回到未定义状态(注意与"空值"不同) | unset VAR;unset -f func(删函数);unset -v VAR(明确删变量) |
readonly | 把变量设为只读,之后再赋值会报错,用于保护不应被脚本改写的常量 | readonly PI=3.14;readonly -p(列出所有只读变量) |
脚本开头写
set -euo pipefail是 Bash 脚本的行业标准做法:它能让脚本在第一个错误处停下来,而不是带着错误状态继续往下跑、把事情搞得更糟。想让-e在某条可能失败的命令上失效,用cmd || true。
二、脚本执行与求值
这几个命令控制"代码怎么被执行"。它们很相似但语义差别很大,用错了往往产生难以察觉的 bug——尤其是 source 与 exec 的区别,一个影响当前 Shell,一个直接替换掉它。
| 命令 | 作用 | 常用示例 |
|---|---|---|
source / . | 在当前 Shell 中读取并执行脚本,不创建子进程,因此脚本里的变量、函数、cd 都会留下来 | source ~/.bashrc;. ./lib.sh(等价写法,POSIX 兼容) |
exec | 用指定命令替换当前进程(PID 不变),常用于替换 Shell 或重定向当前进程的文件描述符 | exec ./app(当前 Shell 变成 app,退出即终端关闭);exec 3< file(打开 fd 3) |
eval | 把参数拼接成字符串再当作命令执行,用于需要二次展开的动态命令,但有注入风险 | eval "$CMD";eval $(ssh-agent)(经典用法) |
command | 绕过同名函数/别名,只执行真正的外部命令或内建命令,避免递归陷阱 | command ls(忽略别名);command -v python3(比 which 更可靠地判断命令是否存在) |
builtin | 强制执行 Shell 内建版本,即使存在同名函数也跳过,与 command 类似但只限内建 | builtin cd /tmp |
caller | 在函数中打印调用者的行号与源文件,用于写日志函数或调试库 | caller 0;echo "$(caller): error" |
eval是双刃剑:如果拼接的字符串里含用户输入,等于给了对方执行任意命令的能力。能用数组或printf解决的,别用eval。判断命令是否存在一律用command -v,不要再用which——后者不是内建,行为在不同系统上不一致。
三、变量声明与作用域
Bash 的变量默认都是全局的,这在写大脚本时很容易造成"函数 A 改了变量,函数 B 莫名其妙变了"的事故。local 是唯一的局部变量机制,且只能在函数内使用。
| 命令 | 作用 | 常用示例 |
|---|---|---|
declare | 声明变量并可选设置属性(整数、数组、只读、导出等),也用于查看变量属性 | declare -i n=5(整数);declare -a arr(索引数组);declare -A map(关联数组);declare -x V(等同 export);declare -r C=1(只读);declare -p VAR(查看属性) |
typeset | declare 的别名,功能完全一致,多见于从 ksh 沿袭下来的老脚本 | typeset -i count |
local | 在函数内声明局部变量,避免污染全局作用域,写函数时应成为默认习惯 | local name="$1";local -a files=() |
数组是 Bash 相对 POSIX shell 的重要增强:
arr=("a" "b")定义,${arr[@]}取全部元素(必须加引号否则空格会炸),${#arr[@]}取长度,${arr[i]}按下标取。忘记给"${arr[@]}"加引号是新手最常见的 bug 来源。
四、参数与输入
| 命令 | 作用 | 常用示例 |
|---|---|---|
shift | 把位置参数整体左移,丢弃 $1,用于逐个处理不定长参数 | shift;shift 2(左移两位,丢弃 $1 $2) |
getopts | 解析短选项(-h、-f file),Shell 内建的参数解析器,比手写 while 循环可靠 | while getopts "hf:" opt; do case $opt in h) usage;; f) FILE=$OPTARG;; esac; done |
read | 从标准输入读一行并赋值给变量,常用于交互式输入或逐行处理文件 | read -p "确认? " ans;read -s pwd(静默,输密码);read -t 5 v(5 秒超时);while read -r line; do ...; done < file(逐行读文件) |
逐行读文件的正确姿势是
while IFS= read -r line; do ...; done < file。IFS=防止行首行尾空白被吞,-r防止反斜杠被当成转义符,重定向(而非管道)避免循环体在子 Shell 里跑导致变量带不出来。
五、条件测试
Shell 里判断文件、字符串、数字,靠的是 test 命令——它返回退出码 0(真)或 1(假),配合 if 使用。[ 是 test 的另一种写法,而 [[]] 是 Bash 的增强版,功能更多、坑更少。
| 命令 | 作用 | 常用示例 |
|---|---|---|
test / [ ] | POSIX 标准条件测试,判断文件属性、字符串、数字比较,注意方括号内两侧必须有空格 | [ -f file ](是普通文件);[ -d dir ];[ -z "$s" ](空串);[ "$a" = "$b" ];[ 1 -lt 2 ] |
[[ ]] | Bash 增强条件测试,支持正则与通配符,且变量不加引号也不会因空值报错 | [[ $s == a* ]](通配);[[ $s =~ ^[0-9]+$ ]](正则);[[ -f a && -d b ]](&&/` |
[ "$a" = "$b" ]里的引号不是可选项:如果$a为空,不加引号会变成[ = "$b" ],Shell 直接报语法错。用[[]]就没这个问题,所以在 Bash 里优先用它。数字比较用-eq/-ne/-lt/-gt,字符串比较用=/!=,不要混用。
六、算术运算
| 命令 | 作用 | 常用示例 |
|---|---|---|
let | 执行算术表达式,变量无需加 $,结果直接赋值;表达式不含空格时可省引号 | let "i=i+1";let i++ |
expr | 外部命令,做整数运算与字符串匹配(老式写法,慢,新脚本建议用 $(( ))) | expr 1 + 2;expr length "abc";expr "abc" : 'a.*' |
true / false | 恒返回成功/失败退出码的内建命令,用于占位循环或强制设置退出码 | while true; do ...; done;`cmd |
现代写法一律用算术展开
$(( )):i=$((i+1))、echo $((3*4))。它比let清晰,比expr快得多(不 fork 进程),还支持++、位运算、进制转换。((i++))作为独立语句时还能直接当条件用。
七、流程控制与退出
| 命令 | 作用 | 常用示例 |
|---|---|---|
exit | 终止当前 Shell 或脚本并返回退出码,0 表示成功,非 0 表示失败 | exit 0;exit 1;exit $?(传递上一条命令的退出码) |
return | 从函数返回(不是退出脚本),可带返回码;只能在函数或 sourced 脚本中使用 | return 0;return 1(函数内表示失败) |
trap | 捕获信号或脚本退出事件并执行指定命令,用于清理临时文件、释放资源 | trap 'rm -f "$tmp"' EXIT(退出时必删临时文件);trap 'echo 中断' INT;trap - EXIT(取消) |
trap ... EXIT是写健壮脚本的关键:无论脚本正常结束、被 Ctrl-C 打断还是因set -e失败退出,清理逻辑都会执行。临时文件应该配合mktemp使用,而不是手写路径——后者在并发运行时会互相踩踏。控制流的完整写法见 Shell 控制流。
八、Shell 内部状态
| 命令 | 作用 | 常用示例 |
|---|---|---|
hash | 记录并显示已执行命令的路径缓存;改动 $PATH 后命令仍找不到旧位置时用它清缓存 | hash(列出缓存);hash -r(清空);hash -d cmd(删单项) |
unalias | 删除别名定义,临时恢复命令的原始行为 | unalias ll;unalias -a(删所有) |
一句话总结
Shell 内建速查:变量用 export/declare/local,执行用 source/exec/eval,参数用 shift/getopts/read,判断用 [[ ]],算术用 $(( )),收尾用 trap——28 个内建命令是 Shell 脚本的地基,它们的共同特点是不 fork 进程,因而能真正改变当前 Shell 的状态。