为 shell 脚本的退出状态使用“保留”代码

Ant*_*gan 19 bash shell-script exit

我最近从高级 Bash 脚本指南中看到了这个具有特殊含义退出代码列表。他们将这些代码称为保留代码,并建议:

根据上表,退出代码 1-2、126-165 和 255 具有特殊含义,因此应避免用于用户指定的退出参数。

不久前,我写了一个脚本,它使用了以下退出状态代码:

  • 0 - 成功
  • 1 - 不正确的主机名
  • 2 - 指定的参数无效
  • 3 - 用户权限不足

当我编写脚本时,我不知道任何特殊的退出代码,所以我只是从第一个错误条件的 1 开始,并为每个连续的错误类型增加退出状态。

我编写脚本的目的是在稍后阶段它可以被其他脚本调用(可以检查非零退出代码)。我实际上还没有这样做;到目前为止,我只从我的交互式 shell (Bash) 运行脚本,我想知道使用我的自定义退出代码会导致什么/是否有任何问题。Advanced Bash-Scripting Guide 中的建议的相关性/重要性如何?

我在 Bash 文档中找不到任何确凿的建议;它关于退出状态的部分只列出了 Bash 使用的退出代码,但没有说明任何这些都是保留的或警告不要将它们用于您自己的脚本/程序。

sch*_*ily 13

没有退出代码具有特殊含义,但 in 值$?可能具有特殊含义。

Bourne Shell 和 ksh93 处理和转发退出代码和错误情况到 shell 变量的方式$?是问题所在。与您列出的相反,只有以下值$?具有特殊含义:

  • 126 无法执行二进制文件,即使它存在
  • 127 指定的二进制文件不存在
  • 128 退出状态是 == 0 但存在一些未指定的问题

此外,还有一个未指定的 shell 和特定于平台的$?代码范围> 128,为被信号中断的程序保留:

  • Bourne Shell bash 和 ksh88 使用 128 + 信号号
  • ksh93 使用 256 + 信号号。

其他值不会产生问题,因为它们可能与 shell 特殊$?值不同。

特别是,值 1 和 2 不用于特殊条件,而只是内置命令使用的退出代码,当它们不是内置命令时,它们的作用相同。因此,您提供的bash 脚本指南的指针似乎不是一本好的手册,因为它只列出了 bash 使用的代码,而没有评论特定代码是否是应避免用于自己的脚本的特殊值。

较新版本的 Bourne Shell 使用waitid()而不是waitpid()等待程序退出,并且waitid()(1989 年为 SVr4 引入)使用更好的系统调用接口(类似于 UNOS 在 1980 年使用的接口)。

当较新的Bourne Shell的版本编码退出原因在一个单独的变量${.sh.code}/${.sh.codename}退出代码即在${.sh.status}/ ${.sh.termsig},请参阅http://schillix.sourceforge.net/man/man1/bosh.1.html,退出代码没有过载具有特殊状态,并且由于使用了`waitid(),Bourne Shell 现在支持返回退出代码的所有 32 位——而不仅仅是低 8 位。

顺便说一句:注意不要在exit(256)C 程序或 shell 脚本中使用或类似,因为这会导致在$?经典 shell中被解释为 0。

  • 相反,我相信是你低估了这里的变异范围,尤其是历史变异。你听起来像是可以依赖 `/bin/sh` 来在这些特殊的退出代码跨平台上保持一致的行为,这是不正确的。(我不*关心*任何特定系统的`/bin/sh` 是否可以说是“真正的 Bourne shell”。更重要的是要知道这些都不在 POSIX 中,并且你引用的大部分内容因为“真正的 Unix 系统”无论如何都不提供符合 POSIX 的 `/bin/sh`。) (4认同)
  • 顺便说一句:我在 5 月下旬针对这个 `waitid()` 错误针对 FreeBSD 和 Linux 内核做了一个错误报告。FreeBSD 人员在 20 小时内修复了问题,Linux 人员对修复他们的错误不感兴趣。...而 Cygwin 的人说他们是一个一个又一个错误的错误 Linux 兼容 ;-) (3认同)
  • 这个答案似乎非常特定于您的某些 SVR4 派生 Unix 的特定变体。请更清楚什么是可移植的,什么不是,请记住,没有“Bourne shell”这样的东西,除非您指的是 V7 中的那个。 (3认同)
  • 单一 Unix 规范_要求_这种行为。有一个 32 位值,是的,但该值包含一个 8 位位字段,其中包含来自 `_exit` 的值的低 8 位。请链接您所指的 FreeBSD 错误报告,也许我误解了您描述的问题。 (2认同)
  • OP 用 [tag:bash] 标记了问题,并在问题文本中提到了 Bash。Bash 是 Bourne 派生的 shell。它不支持 `${.sh.}` 变量。然而,您说的是“Bourne”而不是“Bourne-derived”是真的(尽管您确实包括了 ksh93)。 (2认同)

zwo*_*wol 12

已经多次尝试标准化进程退出代码的含义。除了你提到的那个,我还知道:

  • BSDsysexits.h定义了从 64 开始的值的含义。

  • grep退出代码 0 表示至少找到一个匹配项的GNU文档,1 表示没有找到匹配项,2 表示发生了 I/O 错误;这个约定显然对其他程序也很有用,因为“没有出错,但我什么也没找到”和“发生 I/O 错误”之间的区别是有意义的。

  • C 库函数的许多实现system使用退出代码 127 来指示程序不存在或无法启动。

  • 在 Windows 上,NTSTATUS代码(不方便地分散在 32 位数字空间中)可用作退出代码,特别是那些表明进程因灾难性不当行为(例如STATUS_STACK_OVERFLOW)而终止的代码。

您不能指望任何给定的程序遵守这些约定中的任何特定约定。唯一可靠的规则是退出代码 0 是成功,其他任何事情都是某种失败。(需要注意的是C89的EXIT_SUCCESS没有保证的值为零;但是,exit(0)需要相同的行为来exit(EXIT_SUCCESS)。即使值是不一样的)


PSk*_*cik 8

对于 shell 脚本,我有时会在源代码中sysexist.h使用 shell 保留的退出代码(以 为前缀S_EX_),我将其命名为exit.sh

它基本上是:

EX_OK=0 # successful termination 
EX__BASE=64     # base value for error messages 
EX_USAGE=64     # command line usage error 
EX_DATAERR=65   # data format error 
EX_NOINPUT=66   # cannot open input 
EX_NOUSER=67    # addressee unknown 
EX_NOHOST=68    # host name unknown 
EX_UNAVAILABLE=69       # service unavailable 
EX_SOFTWARE=70  # internal software error 
EX_OSERR=71     # system error (e.g., can't fork) 
EX_OSFILE=72    # critical OS file missing 
EX_CANTCREAT=73 # can't create (user) output file 
EX_IOERR=74     # input/output error 
EX_TEMPFAIL=75  # temp failure; user is invited to retry 
EX_PROTOCOL=76  # remote error in protocol 
EX_NOPERM=77    # permission denied 
EX_CONFIG=78    # configuration error 
EX__MAX=78      # maximum listed value 

#System errors
S_EX_ANY=1      #Catchall for general errors
S_EX_SH=2       #Misuse of shell builtins (according to Bash documentation); seldom seen
S_EX_EXEC=126   #Command invoked cannot execute         Permission problem or command is not an executable
S_EX_NOENT=127  #"command not found"    illegal_command Possible problem with $PATH or a typo
S_EX_INVAL=128  #Invalid argument to exit       exit 3.14159    exit takes only integer args in the range 0 - 255 (see first footnote)                                                                                        
#128+n  Fatal error signal "n"  kill -9 $PPID of script $? returns 137 (128 + 9)                               
#255*   Exit status out of range        exit -1 exit takes only integer args in the range 0 - 255              
S_EX_HUP=129                                                                                                   
S_EX_INT=130   
#...
Run Code Online (Sandbox Code Playgroud)

并且可以通过以下方式生成:

#!/bin/sh
src=/usr/include/sysexits.h
echo "# Generated from \"$src\"" 
echo "# Please inspect the source file for more detailed descriptions"
echo
< "$src" sed -rn 's/^#define  *(\w+)\s*(\d*)/\1=\2/p'| sed 's:/\*:#:; s:\*/::'
cat<<'EOF'

#System errors
S_EX_ANY=1  #Catchall for general errors
S_EX_SH=2   #Misuse of shell builtins (according to Bash documentation); seldom seen
S_EX_EXEC=126   #Command invoked cannot execute     Permission problem or command is not an executable
S_EX_NOENT=127  #"command not found"    illegal_command Possible problem with $PATH or a typo
S_EX_INVAL=128  #Invalid argument to exit   exit 3.14159    exit takes only integer args in the range 0 - 255 (see first footnote)
#128+n  Fatal error signal "n"  kill -9 $PPID of script $? returns 137 (128 + 9)
#255*   Exit status out of range    exit -1 exit takes only integer args in the range 0 - 255
EOF
$(which kill) -l |tr ' ' '\n'| awk '{ printf "S_EX_%s=%s\n", $0, 128+NR; }'
Run Code Online (Sandbox Code Playgroud)

不过,我并不经常使用它,但我确实使用了一个 shell 函数,它将错误代码转换为它们的字符串格式。我给它起了名字exit2str。假设您已命名上述exit.shgenerator exit.sh.sh,则exit2str可以使用 ( exit2str.sh.sh)生成代码:

#!/bin/sh
echo '
exit2str(){
  case "$1" in'
./exit.sh.sh | sed -nEe's|^(S_)?EX_(([^_=]+_?)+)=([0-9]+).*|\4) echo "\1\2";;|p'
echo "
  esac
}"
Run Code Online (Sandbox Code Playgroud)

我在PS1我的交互式 shell 中使用它,以便在我运行每个命令之后,我可以看到它的退出状态和它的字符串形式(如果它确实有一个已知的字符串形式):

[15:58] pjump@laptop:~ 
(0=OK)$ 
[15:59] pjump@laptop:~ 
(0=OK)$ fdsaf
fdsaf: command not found
[15:59] pjump@laptop:~ 
(127=S_NOENT)$ sleep
sleep: missing operand
Try 'sleep --help' for more information.
[15:59] pjump@laptop:~ 
(1=S_ANY)$ sleep 100
^C
[15:59] pjump@laptop:~ 
(130=S_INT)$ sleep 100
^Z
[1]+  Stopped                 sleep 100
[15:59] pjump@laptop:~ 
(148=S_TSTP)$
Run Code Online (Sandbox Code Playgroud)

要获得这些,您需要为 exit2str 函数提供一个资源:

$ ./exit2str.sh.sh > exit2str.sh #Place this somewhere in your PATH
Run Code Online (Sandbox Code Playgroud)

然后在您的~/.bashrc每个命令提示符中使用它来保存和翻译退出代码,并将其显示在您的提示符 ( PS1) 中:

    # ...
    . exit2str.sh
PROMPT_COMMAND='lastStatus=$(st="$?"; echo -n "$st"; str=$(exit2str "$st") && echo "=$str"); # ...'
    PS1="$PS1"'\n($lastStatus)\$'
    # ...                                                                                   
Run Code Online (Sandbox Code Playgroud)

这对于观察一些程序如何遵循退出代码约定而有些不遵循、了解退出代码约定或只是为了能够更容易地看到正在发生的事情非常方便。使用它一段时间后,我可以说许多面向系统的 shell 脚本确实遵循约定。EX_USAGE特别是比较常见,虽然其他代码不多。我不时尝试遵循惯例,尽管总是有$S_EX_ANY(1) 适合懒惰的人(我就是其中之一)。


Cen*_*ane 5

我能找到的最好的参考是这样的:http : //tldp.org/LDP/abs/html/exitcodes.html

根据这个:

1 是一个通用的错误集合,我一直看到它用于用户定义的错误。

2 用于滥用 shell 内置函数,例如语法错误

要直接回答您的问题,您的脚本可以使用保留的错误代码正常运行,假设您根据错误代码 = 1/2/3 处理错误,它将按预期运行。

但是,如果您遇到任何知道并使用保留错误代码的人,这可能会令人困惑,这似乎很少见。

另一个可用的选项是如果有错误则回显错误然后退出,假设您的脚本遵循 Linux 约定“没有消息就是好消息”并且回显成功则无。

if [ $? -ne 0 ];then
    echo "Error type"
    exit 1
fi
Run Code Online (Sandbox Code Playgroud)