菜鸟科技网

Shell 内部命令与外部命令有何本质区别?为何内部命令执行效率更高,且不依赖外部可执行文件?

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

Shell 内部命令与外部命令有何本质区别?为何内部命令执行效率更高,且不依赖外部可执行文件?-图1
(图片来源网络,侵删)

什么是 Shell 内部命令?

Shell 内部命令是指集成在 Shell 解释器本身的命令,而不是作为独立的外部程序文件(如 /bin/ls, /usr/bin/find)存在。

你可以把它们理解为 Shell 解释器自带的“功能函数”或“方法”,当你执行一个内部命令时,Shell 直接调用它自身的代码来执行,而不需要创建一个新的子进程来运行一个外部程序。

  • 外部命令:是磁盘上的一个可执行文件,Shell 找到这个文件后,会启动一个新的进程来运行它。
  • 内部命令:是 Shell 解释器的一部分,Shell 直接执行它,不创建新进程。

为什么需要内部命令?

内部命令的存在主要是为了效率和功能集成

Shell 内部命令与外部命令有何本质区别?为何内部命令执行效率更高,且不依赖外部可执行文件?-图2
(图片来源网络,侵删)
  1. 性能更快:因为不需要创建新的进程,内部命令的执行速度远快于外部命令,对于像 cd, pwd, echo 这样频繁使用的命令,这种性能优势非常明显。
  2. Shell 功能核心:一些命令是 Shell 正常工作所必需的,它们直接操作 Shell 的环境和状态。
    • cd:改变当前工作目录,这个目录信息是 Shell 进程自身的状态,cd 是一个外部命令,它只能在子进程中改变目录,而父进程(你的终端会话)的目录不会改变。
    • exit:退出当前的 Shell 会话,它需要终止 Shell 进程本身,这只能由内部命令完成。
    • source 或 :在当前 Shell 中执行脚本文件,如果它是外部命令,它会在一个子 Shell 中执行,脚本中对环境变量的修改不会影响当前 Shell。
  3. 与 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

使用 whichwhereis 命令

这两个命令主要用于查找命令的可执行文件路径,如果命令是内部命令,它们通常找不到文件。

# 尝试查找 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 中最常用的内部命令列表:

Shell 内部命令与外部命令有何本质区别?为何内部命令执行效率更高,且不依赖外部可执行文件?-图3
(图片来源网络,侵删)
命令 主要功能
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 脚本。

分享:
扫描分享到社交APP
上一篇
下一篇