资讯中心

命令跑完了到底成没成:退出状态这一层

📅 2026/9/29 15:07:47
命令跑完了到底成没成:退出状态这一层
授权与合规声明本文全部操作对象均为自建隔离靶场本机容器或隔离虚拟机涉及安全测试的环节必须以取得合法授权为前提。未经授权的渗透测试违反《中华人民共和国网络安全法》与《刑法》相关条款须承担相应法律责任。本文只讲环境配置、版本对照与靶场隔离不含任何攻击步骤、利用载荷与绕过手法请勿将文中环境指向任何非自有系统。一、命令跑完了屏幕上什么都没说1.1 一个几乎人人都碰过的场景你在自己的隔离虚拟机里敲下一条命令按回车。命令跑完了光标回到下一行的行首屏幕上安安静静一个字符都没有多出来。很多人这时候会停几秒它到底成了没有另一种情形同样常见命令刷出来一大片字看起来像出了问题可它按预期走完了也有的什么都没显示而它并没有按预期走完。更麻烦的是第三种——几条命令串成一条长语句跑完其中某一步没成你从屏幕上完全看不出是哪一步。这几种情形指向同一件事屏幕上有没有字、有多少字跟这条命令成没成不是一回事。本文要讲的就是命令跑完之后用来表示成没成的那个东西——退出状态exit status。它是命令交回给你的反馈里最容易被忽略的一份因为它默认不显示在屏幕上。范围先说清楚本文只讲判定这一层也就是成没成怎么被表示、你怎么把它读出来。屏幕上打印了什么、打印到了哪里是另一层的事本文不碰。1.2 一条命令其实交回了两份东西一条命令结束的时候向你交回的东西可以分成两份。一份是它打印出来的那些字符通常落在终端上。另一份是一个整数它在命令结束的那一刻交给 shell用来表示这次执行的结果。这个整数默认不会自动出现在屏幕上——你不主动去取就看不见它。Bash 官方手册在 3.7.5 节Exit Status里给这个整数下了定义逐字引文见附表 A 第 1 行The exit status of an executed command is the value returned by the waitpid system call or equivalent function.翻成中文一条执行完的命令它的退出状态就是waitpid这个系统调用或等价函数返回的那个值。它不是某个程序自己发明的东西而是命令结束时系统交给它的一个返回值。手册同一句还给了取值范围逐字引文同附表 A 第 1 行Exit statuses fall between 0 and 255, though, as explained below, the shell may use values above 125 specially.退出状态的取值落在 0 到 255 之间不过正如这一节后文会讲到的shell 会把大于 125 的一部分取值另作特殊使用。这两句给出两个要点**它是一个整数而且有边界。**边界这件事第四章会用到。1.3 先把两份反馈分开看最容易混的是把命令打印了什么和命令交回了什么当成一回事屏幕上出现一堆字就以为失败屏幕安静就以为成功。命令交回的东西它落在哪里默认你看得到吗本文是否覆盖命令打印的字符终端或者你让它写去的文件看得到否命令结束时的退出状态交给 shell 的一个整数看不到要主动取是这张表里第二行才是本文的范围。第一行属于内容被写到了哪去的那一层是另一篇文章的事本文不碰。⚠️代码待验证【一条命令的两个出口】 命令跑完 ├─ 出口一打印出来的字符 - 落在终端或你指定的文件里看得见 └─ 出口二退出状态整数 - 交给 shell默认看不见要主动取 本文只讲出口二 这个整数怎么表示成没成取值范围是什么 以及你怎么把它取出来、取出来的又是哪一条命令的。由此可以先记住一条判断**命令跑完了不等于命令成功了。**要判断成没成得去看那个整数。二、为什么是零算成功、非零算失败2.1 第一次看到会觉得反了日常经验里零往往跟没有、空、失败绑在一起余额是零、得分是零听起来都不像好事。在这里刚好相反零表示成功。Bash 官方手册把这件事说得非常直接逐字引文见附表 A 第 2 行For the shell’s purposes, a command which exits with a zero exit status has succeeded. So while an exit status of zero indicates success, a non-zero exit status indicates failure.中文说法对 shell 而言以零退出状态结束的命令就是成功的退出状态为零表示成功非零表示失败。这句话跟很多人的直觉是拧着的所以值得解释一次为什么这么设计。2.2 那句 reason 才是真正要记住的紧跟着的下一句是这一层的核心逐字引文见附表 A 第 3 行This seemingly counter-intuitive scheme is used so there is one well-defined way to indicate success and a variety of ways to indicate various failure modes.逐字读这个看起来违反直觉的方案之所以被采用是为了让成功只有一种定义明确的方式来表示而失败可以有很多种方式来表示不同的失败形态。这句话把设计取舍讲透了成功只需要一个答案失败需要很多个答案。为什么会是这样因为成功只有一种情形——命令按预期走完了没有别的花样而失败可以有很多种参数给得不对、需要的东西不在了、依赖没准备好、执行到一半被打断了。每一种都是不同的失败形态要能区分开就需要足够多的取值来承载。于是取值空间被这样分配成功占掉一个位置剩下的位置留给各种失败那一个位置就落在零上。明白了这一段零表示成功就不再反直觉而是一种把信息量分开的做法。你可能会这样设想官方实际的口径官方给的理由零表示什么都没发生非零表示有结果零表示成功非零表示失败成功只有一种失败有很多种每一种情况都占一个取值成功只占一个取值其余留给各种失败one well-defined way … a variety of ways2.3 落到你身上的第一条判断因为成功只有一个值判断可以做得非常简单先只问一句——这个数是零吗简单有简单的好处它把这条命令成没成从一个要查表的问题变成了一个只要看是不是零的问题判定成功的动作因此没有歧义。但要补一句限制这个简单判断只在两个前提成立时才可靠——你知道自己在看的是哪一条命令的状态第三章以及这条命令遵守常规约定第五章。所以这个结论先记到这里别急着推广。⚠️代码待验证【零与非零的语义照官方口径】 退出状态 0 - 成功唯一的一种表示方式 退出状态 ≠ 0 - 失败可以用很多种取值表示不同的失败形态 所以判断这一步永远只有一个问题这个数是零吗 这个问题的答案之所以这么好给是因为成功只占了一个取值。三、那个数存在哪里$? 取的是哪一条命令3.1 官方对 $? 的定义那个整数不会一直待在那儿等你。它有名字也有覆盖规则。Bash 官方手册在 3.7.5 节里给出的说法是逐字引文见附表 A 第 8 行The exit status of the last command is available in the special parameter $?而在 3.4.2 节Special Parameters里对$?这一项的定义更精确逐字引文见附表 A 第 9 行($?) Expands to the exit status of the most recently executed command.两句话放在一起看关键落在两个词上last和most recently executed——最近执行的那一条命令。这里最近是连续滚动的不是你最关心的那一条。**每执行一条新命令这个位置上的值就被换掉一次。**它像一块随时被覆写的牌子只写着上一次的结果不会把每一条命令的结果分别记下来。3.2 最近这两个字最容易出事先把取值这个动作本身说清楚你在命令行里敲下一条命令去读这个值时读到的其实是它前面那条命令留下的值——因为读取这个值的动作是在一条新命令的准备阶段完成的。由此有两条很实在的结论第一条**你读之前做了什么决定了你读到什么。**如果你想知道刚才那条成没成中间却又敲了别的命令读到的就是后一条的状态跟你想问的那条没关系了。第二条**一块牌子只对应一条命令。**后面的命令会把前面的结果盖掉。所以几条命令串起来跑完读一次就知道是哪一步不成功这个想法在这一层站不住——你读到的只是最后一条的状态。串起来的情况第六章还有一层专门讲。3.3 三个只读动作下面三条动作都只是把那个值取出来看不改动任何东西。⚠️代码待验证# 只读动作一直接把最近执行的那条命令的状态取出来echo$?# 只读动作二先跑一条你自己挑的只读命令紧接着立刻取值command-vbashecho$?# 只读动作三换一条命令再看一次体会覆盖关系typelsecho$?三条动作的共同点是紧跟着取值这个动作离你想问的那条命令越近对应得越准确中途插进来的任何一条命令都会把牌子上的内容换掉。对照关系可以压成一张表你做的事$?里的值属于谁跑一条命令紧接着取值这一条命令跑一条命令中间又跑了一条别的再取值中间那条别的命令跑 A、B、C 三条只在最后取一次值只有 C 这一条一条命令都还没跑就取值待验证本文不判断这种情形下的取值这张表的第四行是有意留下的。没有核到官方逐字口径的情形本文不写结论——附表 A 里也照此标了待验证。本文不贴这三条动作的实际输出。你要做的只是把里面的命令换成你自己的逐条跑一遍看那块牌子在什么时候被换掉——看明白这一点比记住任何一个具体的数是几都重要。四、0 到 255 之间官方点名的几个取值4.1 被信号终止时128 加 N先解释一个词命令结束不一定都是自己走完的也可能是被外部打断的这种打断在系统里用信号来表示。官方口径是逐字引文见附表 A 第 4 行When a command terminates on a fatal signal whose number is N, Bash uses the value 128N as the exit status.中文当一条命令因为编号为 N 的致命信号而终止时Bash 用 128N 作为它的退出状态。这一句有两点值得看。第一这个值不是命令自己决定的而是由那个信号的编号加出来的所以它带有外力的信息。第二它解释了第一章那句shell 会把大于 125 的一部分取值另作特殊使用——128N 这一族取值就落在那一带。信号本身怎么发、发给谁是另一层的事本文只讲它在退出状态这一层留下的形状。4.2 命令没找到、找到但不可执行127 与 126这两个取值放在同一句官方口径里逐字引文见附表 A 第 5 行If a command is not found, the child process created to execute it returns a status of 127. If a command is found but is not executable, the return status is 126.中文如果一条命令没找到为了执行它而创建的子进程返回 127如果一条命令找到了但不能执行返回状态是 126。这两个值是一对差别就在走到哪一步127 是前面那一步就没过去压根没找到这个东西126 是找到了但过不去后面那一步它不能被执行。看到这两个值问题通常不在命令自己的逻辑里而在要执行的东西本身。这里只记这两个取值的口径。“命令是怎么被找到的”“为什么找到了却不能执行”属于别的两层本文不展开。4.3 展开或重定向出错、内建命令用法不对再往下是两类情形。第一类逐字引文见附表 A 第 6 行If a command fails because of an error during expansion or redirection, the exit status is greater than zero.中文如果一条命令因为展开或重定向过程中的问题而失败退出状态大于零。注意官方只给了方向——是大于零没有给固定取值也不能反过来用成某个数字表示某种出错。第二类是 shell 自己实现的那些命令官方把它们称为 builtins内建命令给了两条口径逐字引文见附表 A 第 7 行All of the Bash builtins return an exit status of zero if they succeed and a non-zero status on failureAll builtins return an exit status of 2 to indicate incorrect usage, generally invalid options or missing arguments.中文所有 Bash 内建命令成功时返回退出状态零失败时返回非零所有内建命令在用法不对——一般是选项无效或缺少参数——时返回 2。用处很直接看到 2先去看你给它的参数。把这一章讲到的取值压成一张表取值官方口径见附表 A从这个值你能读出什么0命令成功按预期走下一步2内建命令用法不对选项无效或缺少参数先去看你给它的参数126命令找到了但不能执行不是找不到是过不去127命令没找到前面那一步就没过去128N因编号为 N 的致命信号而终止不是它自己结束的带外力信息大于零展开或重定向出错官方只给方向没有固定取值去看出错的那一步别猜具体数字⚠️代码待验证【本文写到的取值只有这几个照官方口径】 0 成功 2 内建命令用法不对 126 命令找到了但不能执行 127 命令没找到 128N 被编号 N 的致命信号终止 大于零 展开或重定向过程出错官方未给固定取值 注意这张表到此为止。 官方清单里没有的取值本文一条都不编—— 3 表示什么5 表示什么这类映射不写。五、官方自己点名了一份例外清单5.1 Coreutils 手册的常规口径前面几章引的都是 Bash 手册这一章换一份来看同一件事。GNU Coreutils 手册在 2.11 节Exit status里先给了一句大面上的口径逐字引文见附表 A 第 10 行Nearly every command invocation yields an integral exit status that can be used to change how other commands work. For the vast majority of commands, an exit status of zero indicates success. Failure is indicated by a nonzero value – typically ‘1’, though it may differ on unusual platforms as POSIX requires only that it be nonzero.中文几乎每一次命令调用都会产生一个整数的退出状态它可以被用来改变其他命令的行为。对绝大多数命令而言退出状态为零表示成功失败由一个非零值表示——通例是 1但在不常见的平台上可能不同因为 POSIX 只要求它非零。这句话里有两个限定词要特别留意the vast majority绝大多数说明这话不是对所有命令说的typically通例说明 1 只是一个习惯用法不是硬规定。官方在1前面放这两个词就是在提醒别把失败就是 1当成一条规矩。5.2 那份例外清单紧接着的一句是本文最该记住的一条逐字引文见附表 A 第 11 行However, some of the programs documented here do produce other exit status values and a few associate different meanings with the values ‘0’ and ‘1’. Here are the exceptions: chroot, env, expr, ls, nice, nohup, numfmt, printenv, runcon, sort, stdbuf, test, timeout, tty.逐字读不过官方手册收录的有些程序会产生别的退出状态值有少数还会给 0 和 1 这两个值另配一套含义。随后它点名了这批例外。截至 2026-09-27这份清单里是下面这 14 个程序#程序#程序1chroot8printenv2env9runcon3expr10sort4ls11stdbuf5nice12test6nohup13timeout7numfmt14tty这份清单最值得注意的地方不在名字有多少而在它是写手册的人自己主动列出来的官方手册没有说退出状态就两种反而专门开了一句告诉你有一批程序不守这个常规。而且名单里ls、test、sort、env、timeout这些日常用得极多不是几个冷门角落里的特例。这直接回答了为什么不能假设只有 0 和 1**因为这个假设连官方手册自己都不下。**当你把一个非零值直接翻译成失败了可能就跳过了它给这个值的另一层含义。5.3 由此得到的态度不猜⚠️ 待验证每个例外程序具体怎么定义 0 和 1、各自还会产生哪些取值官方这句话没有展开。截至 2026-09-27本文逐字只取到存在这批例外这一条所以正文不给任何一个程序的具体取值含义。真要用到时去查它自己的手册别凭印象下结论。这一点不只适用于清单里的程序清单之外还有多少程序有自己的约定官方没有说本文同样不假设。所以更稳的做法不是记住一张更长的表而是养成一个动作不猜去查。⚠️代码待验证【这份清单怎么用】 第一步我要判断的这个程序是不是只有成 / 不成两种可能 - 不是。官方手册自己点了 14 个例外程序。 第二步它在不在这 14 个里面 - 在去看它自己的手册别拿零与非零往上套。 - 不在也不代表就一定是两种情况——本文不假设只记不猜。 一句话先确认你问的这个程序守不守常规再决定怎么读它的值。配套资料把第一章到第五章讲的这几组取值、以及官方点名的那 14 个例外程序压成一页可以对着查的卡片放在资料包里扫码即可获取六、管道里那么多条命令状态算谁的6.1 默认取最后一条先把管道这个词说清几条命令用一条竖线串起来前一条的输出接到后一条的输入上这样连成的整体叫管道。它看起来像一个动作但在状态这一层它仍然是好几条命令。那么这一串命令跑完交回的那个状态是谁的官方口径是逐字引文见附表 A 第 12 行The exit status of a pipeline is the exit status of the last command in the pipeline, unless the pipefail option is enabled.中文一条管道的退出状态是管道里最后一条命令的退出状态——除非pipefail这个选项被打开了。注意用词官方没有说整条管道的结果说的是最后一条命令的结果——默认情况下前面几条的状态不被算进去。6.2 为什么这件事会咬人要理解前面几条为什么会被忽略得知道管道里的命令怎么跑。官方给的说明是逐字引文见附表 A 第 13 行Each command in a multi-command pipeline, where pipes are created, is executed in its own subshell, which is a separate process.中文在有多条命令的管道里凡是发生连接的地方每条命令都在它自己的子 shell 里执行而子 shell 是一个独立的进程。把这两条合起来看几条命令串起来、不知道是哪一步不成功这件事就有了解释管道里的每条命令都是各自独立跑起来的但你读到的那个状态默认只属于最后一条于是当前面几条不成功、最后一条成功时整条管道交回来的仍然是成功。**这不是你的判断方式出了错而是这一层的默认规则本来就这么定的。**所以这条管道状态是零说明整条链都好好的这句话默认情况下并不成立。6.3 有一个开关会改变它官方那句里留了一个口子unless the pipefail option is enabled——只要pipefail被打开规则就不再是取最后一条。打开之后管道的状态改成看整条管道里最后一个以非零退出的命令只要有任何一条以非零退出整条管道就交回那个非零值只有当所有命令都以零退出时整条管道才交回零。⚠️代码待验证# 只读演示一条管道跑完取到的状态默认属于最后一条命令echodemo|ps/tmp/demo.txtecho$?把开关关着和开关打开的差别压成一张表情形管道的退出状态取自前面几条不成功、最后一条成功时默认开关关着管道里最后一条命令整条管道仍然交回成功pipefail打开最后一个以非零退出的命令整条管道交回那个非零值由此得到一条很实用的提醒判断一条管道成没成之前先确认你要的是哪一个问题——“最后一步成没成”还是整条链条里有没有哪一步没成。这两个问题在这一层有两个不同的答案默认给的是前者。七、把这一层变成动作7.1 一份可以对着用的自检表前面六章讲的是原理这一节把它们压成一份可以对着用的表每一条都是只看不改。⚠️代码待验证【退出状态自检表 · 只看不改】 1. 我读的这个值是最近执行的那条命令的吗 - 读数之前有没有又敲了别的命令 2. 它是不是零 - 零与非零是这一层成败的唯一分野 3. 它是不是 126 或 127 - 分别看命令找到没有和能不能执行 4. 它是不是一个大于 125 的数 - 可能是 128N去看它是不是被信号终止的 5. 它是不是 2 - 去看内建命令的参数用法 6. 我要判断的这个程序守不守常规约定 - 对照第五章那 14 个例外程序 7. 我读的是不是一条管道的状态 - 默认只取最后一条另有一个 pipefail 开关会改这个行为7.2 三条可以带走的动作**先问是不是零再问别的。**零与非零是这一层唯一的分野官方那句 reason 就是为这件事写的。取值紧跟着你要问的那条命令。$?取的是最近执行的那一条中间多敲一条命令就等于换了一个问题。**不猜取值含义。**官方手册自己都点了 14 个例外程序清单里没有的映射本文不写你也别凭印象补。7.3 这份清单不覆盖什么第一**它不解释命令是怎么被找到的。**126 与 127 只讲到这两个取值的口径为止查找机制本身属于另一层。第二**它不解释信号是怎么发出去、发给谁的。**128N 只讲这个值是怎么算出来的信号本身属于另一层。第三**它不给取值含义对照表里清单没有的内容。**本文写到的取值只有 0、2、126、127、128N以及展开或重定向出错时大于零这一条方向性口径其余一律不写附表 A 逐条标了出处。第四**它不给任何实际运行输出。**所有代码块都是本机未实测的只读动作输出随环境不同一条都不贴。第五**它不判断某个程序应该返回什么。**这类约定由程序自己的手册决定不在本文范围内。配套资料把本章这份自检表和第五章那份例外程序清单放在一起做成一张可以先对着用的表放在资料包里扫码即可获取附表 A本文引用事实与官方出处对照表#事实照口径一手出处核验日期本文位置1逐字The exit status of an executed command is the value returned by the waitpid system call or equivalent function. Exit statuses fall between 0 and 255, though, as explained below, the shell may use values above 125 specially.Bash 官方手册 3.7.5 Exit Status — https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html2026-09-27第 1 章2逐字For the shell’s purposes, a command which exits with a zero exit status has succeeded. So while an exit status of zero indicates success, a non-zero exit status indicates failure.同第 1 行2026-09-27第 2 章3逐字This seemingly counter-intuitive scheme is used so there is one well-defined way to indicate success and a variety of ways to indicate various failure modes.同第 1 行2026-09-27第 2 章4逐字When a command terminates on a fatal signal whose number is N, Bash uses the value 128N as the exit status.同第 1 行2026-09-27第 4 章5逐字If a command is not found, the child process created to execute it returns a status of 127. If a command is found but is not executable, the return status is 126.同第 1 行2026-09-27第 4 章6逐字If a command fails because of an error during expansion or redirection, the exit status is greater than zero.同第 1 行2026-09-27第 4 章7逐字All of the Bash builtins return an exit status of zero if they succeed and a non-zero status on failureAll builtins return an exit status of 2 to indicate incorrect usage, generally invalid options or missing arguments.同第 1 行2026-09-27第 4 章8逐字The exit status of the last command is available in the special parameter $?同第 1 行2026-09-27第 3 章9逐字($?) Expands to the exit status of the most recently executed command.Bash 官方手册 3.4.2 Special Parameters — https://www.gnu.org/software/bash/manual/html_node/Special-Parameters.html2026-09-27第 3 章10逐字Nearly every command invocation yields an integral exit status that can be used to change how other commands work. For the vast majority of commands, an exit status of zero indicates success. Failure is indicated by a nonzero value – typically ‘1’, though it may differ on unusual platforms as POSIX requires only that it be nonzero.GNU Coreutils 手册 2.11 Exit status页头标注 GNU Coreutils 9.12— https://www.gnu.org/software/coreutils/manual/html_node/Exit-status.html2026-09-27第 5 章11逐字However, some of the programs documented here do produce other exit status values and a few associate different meanings with the values ‘0’ and ‘1’. Here are the exceptions: chroot, env, expr, ls, nice, nohup, numfmt, printenv, runcon, sort, stdbuf, test, timeout, tty.同第 10 行2026-09-27第 5 章12逐字The exit status of a pipeline is the exit status of the last command in the pipeline, unless the pipefail option is enabled.Bash 官方手册 3.2.3 Pipelines — https://www.gnu.org/software/bash/manual/html_node/Pipelines.html2026-09-27第 6 章13逐字Each command in a multi-command pipeline, where pipes are created, is executed in its own subshell, which is a separate process.同第 12 行2026-09-27第 6 章14一份命令都还没跑过时读取$?得到的值待验证本次未核到官方逐字口径正文不写结论2026-09-27第 3 章待验证15例外清单里每个程序对 0 与 1 各自的具体含义、以及它们还会产生哪些取值待验证官方该句只逐字给出存在这批例外未展开逐个程序的取值含义正文不给具体结论2026-09-27第 5 章待验证16官方这份清单之外是否还有其他程序对退出状态另有约定待验证官方该句只点到这 14 个程序未说明清单外的情况正文不假设2026-09-27第 5 章待验证附表 B术语速查表术语一句话解释退出状态命令结束时交给 shell 的那个整数官方定义为 waitpid 或等价函数的返回值$?Bash 的特殊参数展开为最近执行的那条命令的退出状态零与非零这一层的成败分野零表示成功非零表示失败128N命令因编号为 N 的致命信号终止时Bash 采用的退出状态126 / 127前者是命令找到了但不能执行后者是命令没找到内建命令由 shell 自己实现的命令官方口径是用法不对时返回 2管道用竖线把多条命令串起来、前一条的输出接到后一条输入的写法pipefail打开后管道状态不再取最后一条而是取最后一个以非零退出的命令例外程序官方手册点名的 14 个程序它们给 0 与 1 另配含义或产生其他取值只读动作本文允许的动作范围只看不改不含写入、删除与安装类操作待验证本文中表示截至 2026-09-27 未取到官方逐字依据的标记写在最后这篇用到的资料写这篇文章时我把 Bash 官方手册里讲退出状态、特殊参数和管道的那三节还有 GNU Coreutils 手册里讲退出状态的那一节挨着读了一遍。命令跑完屏幕上什么都没有看着像没消息其实它早就把成没成交回给 shell 了只是默认不显示。顺手也整理了几份配套的东西退出状态速查卡本文写到的几组取值、各自的官方口径与出处例外程序清单卡官方点名的 14 个程序以及不能假设只有两种取值这条提醒靶场环境对照表DVWA、upload-labs 在 Windows / macOS / Linux 三平台的可行性与推荐路径Web 安全学习路线图从基础打牢到安全管理四个阶段各学什么资料是我自己整理的放在下面这个码上扫码即可获取添加时备注「靶场」优先通过。拿到之后建议先看退出状态速查卡那一份先分清屏幕上没字和命令没成是两回事再决定要不要接着跑下一步。

看完文章,想为自己的企业也做一次专业网站诊断?

尧图顾问免费为您评估现有网站,并给出建站/改版建议与报价方案。

免费获取方案