退出代码或退出状态告诉我们最后执行的命令的状态。命令是成功执行还是以错误退出。这些都是可以在命令终止后获得的。

其原理是程序返回退出代码 0  表明程序成功执行且没有问题。代码 1 或任何非 0 的值都被视为不成功。除了 0 和 1 之外,还有更多退出代码,我将在本文中介绍。

Linux shell 退出代码

退出代码 代码的含义
0 命令执行没有错误
1 一般错误代码
2 命令(或参数)使用不正确
126 权限被拒绝(或)无法执行
127 未找到命令,在 PATH 未找到
128+n 通过命令传递从外部信号终止,或者遇到致命错误
130 通过 Ctrl+C 或 SIGINT 终止(终止代码 2 或键盘中断)
143 由 SIGTERM 终止(默认终止)
255/* 退出代码超出 0-255 范围,反转错误

查看命令程序的退出代码

先前执行的命令的退出代码存储在特殊变量 $? 。您可以通过运行命令 echo $? 来查看退出代码:

echo $?

这将在我们所有的演示中用于查看退出代码。值得一提的是,exit 命令支持直接使用与上一个执行的命令相同的退出代码。

退出代码 0

退出代码 0 表示命令执行无误。这是运行命令程序的理想情况。例如,让我们运行neofetch 命令查看系统信息。

neofetch 
echo $?
            .-/+oossssoo+/-.               myfreax@myfreax 
        `:+ssssssssssssssssss+:`           --------------- 
      -+ssssssssssssssssssyyssss+-         OS: Ubuntu 20.04.6 LTS x86_64 
    .ossssssssssssssssssdMMMNysssso.       Kernel: 5.15.0-76-generic 
   /ssssssssssshdmmNNmmyNMMMMhssssss/      Uptime: 3 hours, 41 mins 
  +ssssssssshmydMMMMMMMNddddyssssssss+     Packages: 2316 (dpkg), 14 (snap) 
 /sssssssshNMMMyhhyyyyhmNMMMNhssssssss/    Shell: zsh 5.8 
.ssssssssdMMMNhsssssssssshNMMMdssssssss.   Resolution: 2560x1440 
+sssshhhyNMMNyssssssssssssyNMMMysssssss+   DE: GNOME 
ossyNMMMNyMMhsssssssssssssshmmmhssssssso   WM: Mutter 
ossyNMMMNyMMhsssssssssssssshmmmhssssssso   WM Theme: WhiteSur-Light 
+sssshhhyNMMNyssssssssssssyNMMMysssssss+   Theme: WhiteSur-Light [GTK2/3] 
.ssssssssdMMMNhsssssssssshNMMMdssssssss.   Icons: WhiteSur [GTK2/3] 
 /sssssssshNMMMyhhyyyyhdNMMMNhssssssss/    Terminal: gnome-terminal 
  +sssssssssdmydMMMMMMMMddddyssssssss+     CPU: Intel i5-6500 (4) @ 3.600GHz 
   /ssssssssssshdmNNNNmyNMMMMhssssss/      GPU: NVIDIA GeForce GTX 1050 
    .ossssssssssssssssssdMMMNysssso.       Memory: 5710MiB / 7874MiB 
      -+sssssssssssssssssyyyssss+-
        `:+ssssssssssssssssss+:`                                   
            .-/+oossssoo+/-.                                       
退出代码0(执行成功)

此退出代码 0 意味着指定的命令已成功执行,仅此而已。

退出代码 1

退出代码 1 也是一种常见的代码。它通常意味着命令因一般错误而终止。例如,使用没有 sudo 权限的包管理器会生成代码 1

你可以在 Arch Linux ,尝试运行命令 pacman -Sy,命令将会打印错误代码 1, 意味着运行命令 pacman -Sy 导致错误。

pacman -Sy 
echo $? # 0

如果您在基于 Ubuntu 的发行版不带 sudo 的 apt update 命令,echo $? 则会打印错误代码 100。 这是在没有权限的情况运行 apt 命令的错误代码。

这不是一个标准化的错误代码,而是 apt 特有的错误代码。但我们也可以将其理解为不允许操作。

除此以外,还有一种特殊的情况也会以错误代码 1 退出。那就是在 Shell 的除法中,当使用 0 作为除数时,echo $? 命令打印错误代码 1。

let a="1/0" #zsh: division by zero
echo $?

退出代码 2

当执行的命令有语法错误时,会给出退出代码  2。滥用命令参数也会导致此错误。通常表明该命令由于使用不正确而无法执行。

例如,我们向应该有一个连字符 - 的选项添加两个连字符。echo $? 命令将会打印错误代码 2。

grep --z file.txt
echo $?

还有一种情况是,当因为权限被拒绝时,也会返回错误代码 2。例如使用 ls 命令访问 /root 目录,echo $? 命令将会打印错误代码 2。

ls /root # ls: cannot open directory '/root': Permission denied
echo $? # 2

退出代码 126

126 是一个特殊的退出代码,因为它用于指示由于权限错误而未执行命令或脚本。当您尝试在未授予执行权限的情况执行 shell 脚本时,可能会出现此错误。

./script.sh # bash: ./script.sh: Permission denied
echo $? # 126

请注意,此退出代码仅在没有足够权限的情况执行脚本 / 命令时出现,这与一般的权限被拒绝错误不同。

因此,不要将其与在退出代码 2 中看到的 ls 命令访问根目录示例混淆。运行 ls 命令访问根目录的权限问题是来自 ls 命令尝试执行的目录。在这里,权限问题来自脚本本身。

退出代码 127

这是另一种常见的情况。退出代码 127 指的是命令未找到。当执行的命令中有拼写错误或所需的可执行文件不在 $PATH 环境变量时,通常会发生这种情况。

例如,当我们没有使用相对路径或者绝对路径运行脚本时,则经常会看到此错误。

./script.sh # 正常执行
script.sh # bash: script.sh: command not found
echo $? # 127

或者当您尝试运行的可执行文件在 $PATH 环境变量指定的目录中未找到。您可以可执行文件所在的目录添加到 PATH 变量来解决此问题。

当您键入不存在的命令时,您也会收到此退出代码。

unmount -a # bash: unmount command not found
echo $? # 127

退出代码 128+n

当应用程序或命令因致命错误而终止或执行失败时,将生成与 128 相邻的代码 128+n,其中 n 是信号编号。此处的 n 值适用于所有类型的终止代码,例如 SIGTERMSIGKILL 等。

代码 130 或 SIGINT

SIGINT 或键盘中断信号是通过终止信号 2 或 Ctrl+C 中断进程而引发的。由于终止信号为 2,因此我们得到代码 130 (128+2)。

lxappearance # 打开任意一个 GUI 应用,通过终端
echo $? # 130

代码 137 或 SIGKILL

立即终止进程的 SIGKILL 终止信号 9。这是终止应用程序时应使用的最后一种方法。由于终止信号为 9 (128+9),因此命令 echo $? 打印退出代码 137。

lxappearance # 打开任意一个 GUI 应用,通过终端
pkill lxappearance #在另一个终端,终止 lxappearance  GUI 应用
echo $? # 130 在打开的 lxappearance 应用在同一个终端运行此命令
📋
请注意,如果从启动进程的同一会话终止,这些信号可能不会出现。如果您要复现这些例子,请从不同的 shell 终止。就个人而言,信号 128 无法再现。

超过 255 的错误代码

最新版本的 Bash 保留原始退出代码值,甚至超过 255,但通常,如果代码超过 255,则它会被反转。

也就是说,代码 256 变为 0,257 变为 1,383 变为 127,依此类推。为了确保更好的兼容性,请将退出代码保持在 0 到 255 之间。

结论

至此,您已了解 Linux shell 的退出代码。使用它们可以方便地解决各种问题。如果您在 shell 脚本中使用这些代码,请确保您了解每个代码的含义,则可以更轻松地进行调试。