内置命令(builtin)由 shell 自身实现,不派生子进程。它们能直接改变当前 shell 的状态(变量、目录、任务),这也是 cdsource 必须内置的原因。

command

command - 绕过 shell 函数和别名,直接执行同名的外部命令或内置命令

常用命令

  • command ls 即使 ls 被 alias 成 ls -G,也执行原始的 ls
  • command -v git 查询命令的路径或定义(脚本里判断命令是否存在的标准写法)
  • command -v node >/dev/null 2>&1 || echo "node 未安装" 脚本中做依赖检查
  • command -V cd 显示命令类型的详细描述(builtin / alias / function / file)
  • command -p getconf PATH 用系统默认 PATH 查找命令,避免被用户 PATH 干扰

语法格式

command [-pVv] command [arg ...]

OPTIONS

  • -v 打印命令的路径或定义(类似 which,但对 alias/function 也有效)
  • -V 打印更详细的类型说明
  • -p 使用系统默认的 PATH 搜索,而不是当前环境的 PATH

备注

  • which 的区别:which 是外部程序、只搜 PATH,看不到 alias/function/builtin;脚本里检测命令是否存在应优先用 command -v
  • 在函数里调用同名外部命令时(如包装 cd 的函数内部),必须用 command cd 防止无限递归

echo

echo - 将文本输出到标准输出

常用命令

  • echo "Hello World" 输出一行文本并自动换行
  • echo $PATH 查看变量的值(最快的环境变量查看方式)
  • echo -n "no newline" 输出后不追加换行符,常用于拼接输出
  • echo -e "line1\nline2\ttab" 解释转义字符(\n 换行、\t 制表符)
  • echo "content" > file.txt 覆盖写入文件(快速创建小文件)
  • echo "export PATH=\$PATH:/new/bin" >> ~/.zshrc 追加一行内容到文件末尾
  • echo $? 输出上一条命令的退出码,脚本排错常用
  • echo {1..5} 配合花括号展开输出 1 2 3 4 5
  • echo * 输出当前目录所有文件名(通配符由 shell 展开)
  • echo -e "\033[31mred\033[0m" 输出 ANSI 颜色文本

语法格式

echo [OPTIONS] [STRING...]

OPTIONS

  • -n 不在末尾输出换行符
  • -e 启用反斜杠转义解释
  • -E 禁用转义解释(GNU echo 默认行为)

常用转义序列(配合 -e):

  • \n 换行
  • \t 水平制表符
  • \\ 反斜杠本身
  • \033(或 \e)ESC 字符,用于 ANSI 颜色控制
  • \c 到此为止不再输出(含换行),效果类似 -n

备注

  • echo 既是 shell 内置命令也有外部程序 /bin/echo,平时执行的是内置版本;各实现对 -e/-n 支持不一致:macOS 的 /bin/echo(BSD)不支持 -e,zsh 内置 echo 则默认就解释转义。可移植脚本建议改用 printf,如 printf '%s\n' "$str"
  • 输出变量务必加双引号:echo "$var",否则值里的连续空格会被压缩、* 等字符会被通配展开
  • 输出以 - 开头的任意内容时 echo 会把它当选项,此场景用 printf '%s\n' "$data"

eval

eval - 将参数拼接成一条命令,由 shell 重新解析并执行

常用命令

  • eval "$(ssh-agent -s)" 执行 ssh-agent 输出的环境变量设置语句(最常见用法)
  • eval "$(brew shellenv)" 加载 Homebrew 的环境变量(macOS 配置文件常见)
  • eval "$(pyenv init -)" 初始化 pyenv / rbenv / direnv 这类需要注入 shell 函数的工具
  • cmd="ls -l /tmp"; eval "$cmd" 执行存放在变量里的完整命令(含管道、重定向时必须 eval)
  • eval echo \$$varname 间接取变量值:varname 存的是另一个变量名(bash 更推荐 ${!varname}
  • eval "$name=hello" 动态给「名字存在变量里」的变量赋值(bash 也可用 declare "$name=hello"
  • for f in *.log; do eval "gzip '$f'"; done 拼接后再解析一次,让变量中的引号、重定向生效

语法格式

eval [ARGUMENTS...]

OPTIONS

eval 是 shell 内置命令,没有选项:

  • ARGUMENTS 所有参数以空格连接成一个字符串,再作为 shell 命令完整走一遍解析(变量展开、通配、引号、重定向、管道)并执行
  • 退出码为被执行命令的退出码;参数为空时返回 0

备注

  • 安全风险:eval 会二次解析字符串,任何来自用户输入 / 外部数据的内容进入 eval 都可能造成命令注入,如 eval "ls $userinput" 且输入为 ; rm -rf ~。原则:能不用就不用,必须用时严格控制输入来源
  • 与直接执行 $cmd 的区别:$cmd 只做变量展开和分词,字符串里的 |>&&、引号不会被解释;eval 会完整再解析一遍,因此「变量里存整条命令」的场景才需要 eval
  • 多数间接引用场景有更安全的替代:bash 的 ${!var}declare -n(nameref)、数组存命令 "${cmd[@]}"

history

history - 查看和管理当前 shell 的命令历史

常用命令

  • history 列出全部历史命令(带编号)
  • history 20 只看最近 20 条
  • history | grep ssh 搜索历史中包含 ssh 的命令
  • !100 重新执行编号为 100 的历史命令
  • !! 重新执行上一条命令,典型用法 sudo !!
  • !ssh 重新执行最近一条以 ssh 开头的命令
  • !$ 引用上一条命令的最后一个参数,如 mkdir /data/logs 后接 cd !$
  • Ctrl+R 交互式反向搜索历史(输入关键词增量匹配,回车执行)
  • history -c 清空当前会话的内存历史
  • history -d 100 删除编号 100 的那条历史(如误输了密码)
  • history -w 立即把内存中的历史写入历史文件(bash 默认退出时才写)
  • fc -l -10 POSIX 方式列最近 10 条;fc 不带参数则用编辑器打开上一条命令改完再执行

语法格式

history [N]
history [-c] [-d OFFSET]
history [-anrw] [FILENAME]
history [-ps] ARG...

OPTIONS

  • N 只显示最近 N 条
  • -c 清空内存中的历史列表
  • -d OFFSET 删除指定编号的历史条目
  • -a 把本会话新增的历史追加到历史文件
  • -r 读取历史文件内容追加到当前内存列表
  • -w 把内存历史整体写入历史文件(覆盖)
  • -n 读取历史文件中尚未加载的新行(多终端同步)
  • -p 只做历史展开并打印结果,不执行、不入史
  • -s 把参数作为一条历史记录追加(不执行)

备注

  • 相关环境变量:HISTFILE(历史文件,bash 默认 ~/.bash_history,zsh 常配 ~/.zsh_history)、HISTSIZE(内存条数)、HISTFILESIZE / zsh 的 SAVEHIST(文件条数)、HISTCONTROL=ignoredups:ignorespace(bash:去重、以空格开头的命令不入史——临时敏感命令前加个空格即可)
  • bash 默认退出时才把历史写入文件,多窗口会互相覆盖;可用 PROMPT_COMMAND='history -a' 实时追加。zsh 对应 setopt SHARE_HISTORY / INC_APPEND_HISTORY
  • 以上选项为 bash 内置版本;zsh 的 historyfc -l 的别名,-c/-d 等选项不通用,zsh 清空历史需操作 HISTFILE 或用 fc -p

kill

kill - 向进程发送信号(默认 TERM,常用于终止进程)

常用命令

  • kill 1234 向 PID 1234 发送 SIGTERM,请求进程正常退出(可被捕获、做清理)
  • kill -9 1234 发送 SIGKILL 强制杀死,进程无法捕获或忽略(最后手段)
  • kill -15 1234 显式发送 SIGTERM,等价于不带参数
  • kill -HUP 1234 发送 SIGHUP,很多守护进程(nginx 等)约定收到后重新加载配置
  • kill -STOP 1234 / kill -CONT 1234 暂停 / 恢复进程(不可捕获的挂起)
  • kill -l 列出所有信号名与编号
  • kill -0 1234 不发送任何信号,仅探测进程是否存在及是否有权限(脚本判活常用)
  • kill -TERM -1234 PID 为负数时向整个进程组 1234 发信号
  • kill %1 杀掉当前 shell 的 1 号后台作业(jobs 查看)
  • pkill -f "python server.py" 按命令行匹配杀进程;pgrep -f xxx 先确认再杀更稳妥
  • killall nginx 按进程名杀掉所有同名进程

语法格式

kill [-SIGNAL | -s SIGNAL | -n SIGNUM] PID...
kill -l [SIGNUM]

OPTIONS

  • -SIGNAL 信号名或编号,如 -9-KILL-SIGKILL 等价
  • -s SIGNAL 以选项形式指定信号名
  • -n SIGNUM 以选项形式指定信号编号
  • -l 列出信号;kill -l 9 反查编号对应的信号名
  • PID 目标进程号;0 当前进程组;-1 所有有权限的进程(危险);-N 进程组 N;%N 作业号

常用信号速查:

  • 1 SIGHUP 挂断/重载配置
  • 2 SIGINT 中断(等价 Ctrl+C)
  • 9 SIGKILL 强制杀死,不可捕获
  • 15 SIGTERM 默认信号,请求正常退出
  • 18 SIGCONT / 19 SIGSTOP 恢复 / 暂停(Linux 编号;macOS 为 19/17)

备注

  • 优先 kill(TERM)给进程清理机会,几秒无效再 kill -9;直接 -9 可能留下未落盘数据、僵尸子进程或残留锁文件
  • kill -9 杀不掉的进程通常处于 D 状态(不可中断的内核 IO 等待)或为 zombie(僵尸进程要靠父进程回收,杀父进程才消失)
  • kill 既是 shell 内置命令又有外部程序 /bin/kill,内置版本才支持 %N 作业号;信号编号在 Linux 与 macOS/BSD 间有差异(如 SIGSTOP),跨平台脚本用信号名不用编号

set

set - 设置 shell 运行选项,或查看/重置位置参数与变量

常用命令

  • set -e 任一命令失败(非零退出码)立即终止脚本,脚本头部标配
  • set -x 执行前打印每条命令(带展开后的实参),调试脚本利器
  • set -u 引用未定义变量时报错退出,避免 rm -rf $DIR/ 之类事故
  • set -o pipefail 管道中任一环节失败即视为整条管道失败(bash/zsh)
  • set -euxo pipefail 严格模式组合,常作为 bash 脚本第一行配置
  • set +x 关闭命令回显(+ 表示关闭,与 - 相反)
  • set -- a b c 重置位置参数,之后 $1=a $2=b $3=c
  • set -- 清空全部位置参数
  • set 不带参数:列出当前 shell 的所有变量和函数
  • set -o 列出所有选项及当前开/关状态

语法格式

set [-abefhkmnptuvxBCEHPT] [-o OPTION] [ARGUMENTS...]
set [+abefhkmnptuvxBCEHPT] [+o OPTION] [ARGUMENTS...]
set -- [ARGUMENTS...]

OPTIONS

  • -e 命令失败即退出(errexit);条件判断(if/&&/||)中的失败不触发
  • -u 使用未定义变量视为错误(nounset)
  • -x 回显执行的命令及展开结果(xtrace)
  • -v 原样回显读入的每一行(verbose,不做展开)
  • -n 只做语法检查不执行(noexec),bash -n script.sh 同效
  • -f 禁用文件名通配展开(noglob)
  • -C 禁止 > 覆盖已存在文件(noclobber),需覆盖时用 >|
  • -o pipefail 管道退出码取第一个失败命令的退出码
  • -o OPTION 长名形式:errexit/nounset/xtrace/noglob 等与短选项一一对应
  • -- 其后的参数全部作为新的位置参数,即使以 - 开头

备注

  • - 开启、+ 关闭,方向与直觉相反,如 set +e取消「出错即退」
  • setenv/export 的区别:set 显示的是 shell 全部变量(含未 export 的)和选项开关;设置环境变量用 export VAR=value,set 不用于赋值(set VAR=value 在 bash 里只会改位置参数——这是从 csh 迁移过来的常见误用)
  • set -e 有不少坑:命令位于 ifwhile&&/|| 左侧、! 之后时失败不会退出;子 shell 与命令替换中的行为随 bash 版本有差异,关键路径仍应显式判断退出码

source

source - 在当前 shell 中读取并执行脚本文件,不产生子进程

常用命令

  • source ~/.zshrc 修改配置文件后立即生效,无需重开终端
  • . ~/.bashrc. 是 source 的 POSIX 等价写法,效果完全相同
  • source /etc/profile 重新加载系统级环境变量配置
  • source venv/bin/activate 激活 Python 虚拟环境(脚本要修改当前 shell 环境,必须 source)
  • source .env.env 中的变量赋值导入当前 shell(变量需 export 才能传给子进程)
  • set -a; source .env; set +a 导入 .env 并自动 export 全部变量,供后续命令使用
  • source script.sh arg1 arg2 带位置参数执行脚本,脚本内可用 $1$2 取值(bash/zsh 支持)
  • [ -f ~/.aliases ] && source ~/.aliases 文件存在才加载,常写在配置文件里避免报错

语法格式

source FILENAME [ARGUMENTS...]
. FILENAME [ARGUMENTS...]

OPTIONS

source 是 shell 内置命令,本身没有选项开关,参数含义如下:

  • FILENAME 要读取执行的脚本路径;文件名不含 / 时 bash 会先在 $PATH 中查找,用 ./file 可强制当前目录
  • ARGUMENTS 传给脚本的位置参数;省略时脚本沿用当前 shell 的位置参数

相关的 shell 开关:

  • shopt -s sourcepath / shopt -u sourcepath bash 中控制是否在 $PATH 里查找被 source 的文件(默认开启)

备注

  • bash script.sh(子进程执行)的核心区别:source 在当前 shell 中执行,脚本里的变量赋值、cd、函数定义都会留在当前会话;子进程执行则全部随进程结束丢弃。这也是激活 venv 必须 source 的原因
  • 被 source 的脚本里执行 exit 会直接退出当前 shell(终端被关掉),脚本内应改用 return
  • .(点命令)是 POSIX 标准写法,source 是 bash/zsh 扩展;写 #!/bin/sh 的可移植脚本时只能用 .

test

test - 条件求值:判断文件属性、字符串与数值关系,以退出码表示真假

常用命令

  • test -f /etc/hosts && echo exists 判断普通文件是否存在
  • [ -d /data ] || mkdir /data[ 是 test 的等价写法(最后一个参数必须是 ]),目录不存在则创建
  • [ -z "$var" ] && echo "var 为空" 判断字符串为空
  • [ -n "$var" ] 判断字符串非空
  • [ "$a" = "$b" ] 字符串相等判断(注意是单个 =,两边留空格)
  • [ "$num" -gt 10 ] 数值比较:大于(-eq -ne -lt -le -gt -ge
  • [ -x script.sh ] && ./script.sh 有可执行权限才运行
  • [ -s error.log ] 判断文件存在且非空
  • [ file1 -nt file2 ] 判断 file1 比 file2 更新(newer than),增量构建常用
  • [ -e "$path" -a -w "$path" ] 多条件与(POSIX 已不推荐 -a/-o,建议 [ -e "$p" ] && [ -w "$p" ]
  • [[ $str == foo* ]] bash/zsh 扩展 [[ ]]:支持通配匹配、=~ 正则,且变量不加引号也不分词

语法格式

test EXPRESSION
[ EXPRESSION ]
[[ EXPRESSION ]]    # bash/zsh 扩展关键字

OPTIONS

文件判断:

  • -e FILE 存在(任意类型)
  • -f FILE 是普通文件
  • -d FILE 是目录
  • -L FILE 是符号链接
  • -s FILE 存在且大小大于 0
  • -r / -w / -x FILE 可读 / 可写 / 可执行
  • F1 -nt F2 / F1 -ot F2 F1 比 F2 新 / 旧

字符串与数值:

  • -z STR / -n STR 为空 / 非空
  • STR1 = STR2 / STR1 != STR2 字符串相等 / 不等
  • N1 -eq N2 数值相等,另有 -ne -lt -le -gt -ge
  • ! EXPR 逻辑取反

备注

  • 变量务必加双引号:[ -f "$file" ];不加引号时空值会让表达式塌缩成 [ -f ] 引发误判或语法错误。[[ ]] 内无此问题
  • 字符串比较用 === 仅是 bash 扩展;数值比较必须用 -eq 系列,[ 10 > 9 ] 里的 > 会被当成重定向([[ ]] 内则是字符串字典序比较,[[ 10 > 9 ]] 结果也不是数值比较)
  • [ 要求最后一个参数是 ],且 [] 与表达式之间必须有空格;[[ ]] 是 shell 关键字非命令,功能更强(&&||=~ 正则、通配)但不是 POSIX,#!/bin/sh 脚本只能用 test/[