这是一个在 Linux/Unix 系统管理中非常基础且重要的概念,理解它对于高效使用 Shell 和排查问题至关重要。

什么是 Shell 内部命令?
Shell 内部命令是指集成在 Shell 解释器本身的命令,而不是作为独立的外部程序文件(如 /bin/ls, /usr/bin/find)存在。
你可以把它们理解为 Shell 解释器自带的“功能函数”或“方法”,当你执行一个内部命令时,Shell 直接调用它自身的代码来执行,而不需要创建一个新的子进程来运行一个外部程序。
- 外部命令:是磁盘上的一个可执行文件,Shell 找到这个文件后,会启动一个新的进程来运行它。
- 内部命令:是 Shell 解释器的一部分,Shell 直接执行它,不创建新进程。
为什么需要内部命令?
内部命令的存在主要是为了效率和功能集成。

- 性能更快:因为不需要创建新的进程,内部命令的执行速度远快于外部命令,对于像
cd,pwd,echo这样频繁使用的命令,这种性能优势非常明显。 - Shell 功能核心:一些命令是 Shell 正常工作所必需的,它们直接操作 Shell 的环境和状态。
cd:改变当前工作目录,这个目录信息是 Shell 进程自身的状态,cd是一个外部命令,它只能在子进程中改变目录,而父进程(你的终端会话)的目录不会改变。exit:退出当前的 Shell 会话,它需要终止 Shell 进程本身,这只能由内部命令完成。source或 :在当前 Shell 中执行脚本文件,如果它是外部命令,它会在一个子 Shell 中执行,脚本中对环境变量的修改不会影响当前 Shell。
- 与 Shell 环境紧密集成:内部命令可以方便地访问和修改 Shell 的核心数据结构,如变量、选项、函数等。
如何区分内部命令和外部命令?
有几种非常实用的方法可以区分它们。
使用 type 命令(推荐)
type 命令是专门用来显示命令类型的。
# 查看 cd 命令 $ type cd cd is a shell builtin # 输出表明 cd 是一个内部命令 # 查看 ls 命令 $ type ls ls is aliased to `ls --color=auto' # ls 是一个别名,但它的底层实现是外部命令 # 我们可以再看看别名的底层 $ type -a ls ls is aliased to `ls --color=auto' ls is /usr/bin/ls # 这里显示了 ls 的实际位置,是一个外部命令 # 查看 echo 命令 $ type echo echo is a shell builtin # echo 通常是内部命令 # echo 也可能是外部命令 $ type -a echo echo is a shell builtin echo is /usr/bin/echo # 在某些系统或特定情况下,也存在外部的 echo
使用 which 和 whereis 命令
这两个命令主要用于查找命令的可执行文件路径,如果命令是内部命令,它们通常找不到文件。
# 尝试查找 cd $ which cd which: no cd in (/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games) # 找不到,因为没有 cd 文件 # 尝试查找 ls $ which ls /usr/bin/ls # 找到了,显示其路径 # whereis 也是类似,但会查找更多相关的文件 $ whereis cd cd: /usr/bin/cd /usr/share/man/man1/cd.1.gz # 哇,这里找到了! # 注意:有些系统会在 /usr/bin 下为内部命令创建一个“桩”文件,以便 `which` 和 `whereis` 能找到它,但执行它时,Shell 仍然会优先使用内部版本,`type` 是最准确的判断方法。
使用 man 命令
# 查看 cd 的手册页
$ man cd
CD(1) User Commands CD(1)
NAME
cd - change directory
SYNOPSIS
cd [-L|[-P [-e]]] [dir]
...
# 注意,手册页的标题是 `CD(1)`,`(1)` 代表用户命令,这并不绝对区分内外。
# 查看 ls 的手册页
$ man ls
LS(1) User Commands LS(1)
...也是 `LS(1)`。
# `man` 不能直接区分,但内部命令的手册页通常更简洁,因为它描述的是 Shell 的行为。
常见的 Shell 内部命令
下面是一些在 Bash 和其他常见 Shell 中最常用的内部命令列表:

| 命令 | 主要功能 |
|---|---|
或 source |
在当前 Shell 中读取并执行文件,而不是创建子 Shell。 |
alias |
创建或显示命令别名。 |
bg |
将一个挂起的任务放到后台继续运行。 |
cd |
更改当前工作目录。 |
command |
执行指定的命令,并忽略任何该名称的函数或别名。 |
echo |
显示一行文本(通常由内部命令实现,但外部版本也存在)。 |
exit |
终止当前的 Shell 会话。 |
export |
将变量设置为环境变量,使其可以被子进程继承。 |
false |
返回一个非零的退出状态码(代表失败),通常用于脚本测试。 |
fg |
将一个后台任务调到前台继续运行。 |
jobs |
显示当前 Shell 会话中所有正在运行或已停止的任务。 |
kill |
发送信号来终止进程(kill 也是一个外部命令,但内部版本用于处理任务控制)。 |
pwd |
打印当前工作目录的完整路径。 |
read |
从标准输入读取一行,并将其赋给一个或多个变量。 |
return |
从一个函数或脚本中退出。 |
set |
设置或取消 Shell 选项,或显示 Shell 变量和函数。 |
shift |
将位置参数向左移动一位。 |
test |
或其单括号形式 [,用于评估条件表达式。 |
true |
返回零的退出状态码(代表成功),通常用于脚本测试。 |
type |
显示命令的类型(内部、外部、别名等)。 |
ulimit |
设置或显示用户对资源的使用限制(如最大文件大小、最大进程数)。 |
unalias |
删除一个已定义的别名。 |
unset |
删除一个 Shell 变量或函数。 |
内部命令 vs. 外部命令:一个关键示例 (cd)
这是理解两者区别最经典的例子。
假设你的当前目录是 /home/user。
# 1. 尝试使用外部命令 cd (如果存在) # 假设 /usr/bin/cd 存在 $ /usr/bin/cd /tmp # 2. 检查当前目录 $ pwd /home/user # 目录没有改变!
为什么?
因为 /usr/bin/cd 是一个外部程序,当你运行它时,Shell 创建了一个子进程来执行这个程序,这个子进程改变了自己的当前工作目录到 /tmp,然后退出,你的主 Shell 进程(你与之交互的那个)的当前工作目录仍然是 /home/user。
而当你直接输入 cd 时,你调用的是 Bash 的内部命令,它在当前 Shell 进程内部直接修改了工作目录状态,pwd 立刻就能看到变化。
# 1. 使用内部命令 cd $ cd /tmp # 2. 检查当前目录 $ pwd /tmp # 目录成功改变了!
| 特性 | Shell 内部命令 | 外部命令 |
|---|---|---|
| 本质 | Shell 解释器的一部分 | 独立的可执行文件 |
| 执行方式 | Shell 直接调用,不创建新进程 | Shell 创建一个子进程来执行 |
| 性能 | 快 | 相对较慢 |
| 功能 | 管理 Shell 自身状态(如目录、变量、作业) | 执行具体的系统任务(如文件操作、网络通信) |
| 如何查找 | type <command> 通常显示 shell builtin |
which <command> 或 whereis <command> 能找到文件路径 |
| 典型例子 | cd, pwd, source, export, alias |
ls, cp, mv, rm, find, grep |
掌握内部命令和外部命令的区别,能帮助你更深刻地理解 Shell 的工作原理,写出更健壮、更高效的 Shell 脚本。
