シェルスクリプトの終了ステータスに「予約済み」コードを使用する


15

最近、Advanced Bash-Scripting Guideのこの特別な意味を持つ終了コードのリストに出会いました。彼らはこれらのコードを予約済みとして参照し、以下を推奨しています。

上記の表によると、終了コード1-2、126-165、および255は特別な意味を持っているため、ユーザー指定の終了パラメーターには使用しないでください。

少し前に、次の終了ステータスコードを使用するスクリプトを作成しました。

  • 0-成功
  • 1-間違ったホスト名
  • 2-無効な引数が指定されました
  • 3-ユーザー権限が不十分

スクリプトを作成したとき、特別な終了コードを認識していなかったため、最初のエラー状態で1から開始し、連続するエラータイプごとに終了ステータスを増やしました。

後の段階で、他のスクリプト(ゼロ以外の終了コードをチェックできる)から呼び出せるように、スクリプトを作成しました。私は実際にまだそれをしていません。これまでのところ、インタラクティブシェル(Bash)からスクリプトを実行しただけで、カスタム終了コードを使用すると問題が発生する可能性があるかどうかを疑問に思っていました。Advanced Bash-Scripting Guideの推奨事項はどの程度重要/重要ですか?

Bashのドキュメントには裏付けとなるアドバイスが見つかりませんでした。終了ステータスのセクションには、Bashが使用する終了コードがリストされているだけですが、これらのいずれも予約済みではなく、独自のスクリプト/プログラムで使用することを警告するものでもありません。


6
私や他の人は、ABSGの品質は一般に低いと考えています。私の意見では、リンクしたページの作成者は、シェル自体が特定の意味でそれらを使用しているという事実に基づいて、リストされた終了コードが予約されているというサポートされていない主張をしています。スクリプトの標準を作成する試みが行われましたが、いずれも成功していません。重要なことは、選択したエラーコードを文書化して、スクリプトの消費者(他のスクリプトなど)がそれらに基づいて何をすべきかを知ることです。
追って通知があるまで一時停止します。

@DennisWilliamsonコメントとして回答を投稿していただければ、喜んで賛成です。私はすでに他のすべての回答に投票しましたが、それらのそれぞれが有用であることがわかりました。あなたの答えの内容はデビッド・キング(および程度は少ないがズウォル)に似ていますが、あなたはABSG引用に主張の証拠がないことを明示的に述べています。
アンソニーG-モニカの正義

1
申し出をありがとうございますが、私のコメントはそのままであると信じています。
追って通知があるまで一時停止します。

それ以来、POSIX仕様に同様のアドバイスが含まれていることを発見したため、その情報を自分の回答に追加しました(この質問をしてからの調査結果を含む)。
アンソニーG-モニカの正義

回答:


10

プロセス終了コードの意味を標準化しようとする試みがいくつかありました。あなたが言及したものに加えて、私は知っています:

  • sysexits.h64以上の値の意味を定義するBSDがあります。

  • grepコード0を終了するGNU ドキュメントは、少なくとも1つの一致が見つかったことを意味し、1は一致が見つからなかったことを意味し、2はI / Oエラーが発生したことを意味します。この規則は、「何も問題がなかったが何も見つからなかった」と「I / Oエラーが発生した」との区別が意味のある他のプログラムにも明らかに役立ちます。

  • Cライブラリ関数の多くの実装では、system終了コード127を使用して、プログラムが存在しないか、起動に失敗したことを示します。

  • Windowsでは、NTSTATUSコード(32ビットの数値スペース全体に不便に散らばっている)を終了コードとして使用できます。特に、壊滅的な誤動作(たとえばSTATUS_STACK_OVERFLOW)によりプロセスが終了したことを示すものです。

これらの規約の特定の1つに従う特定のプログラムを当てにすることはできません。唯一の信頼できるルールは、終了コード0が成功であり、それ以外は何らかの失敗であるということです。(C89者がいることを注意EXIT_SUCCESSされていないゼロ値を持つことが保証;しかし、exit(0)に同じ動作をするために必要とされるexit(EXIT_SUCCESS)値が同じでない場合でも)。


ありがとう。他の回答よりも1つの回答を選択することは困難でしたが、使用中のさまざまな終了コードの幅広いフレーバーを提供しながら(関連リンクを使用して)私の質問に答えたため、私はこれを受け入れています:それは3つ以上の賛成票に値する現在持っています。
アンソニーG-モニカの正義

11

特別な意味を持つ終了コードはありませんが、の値に$?は特別な意味がある場合があります。

Bourne Shellとksh93が終了コードとエラー状況を処理してシェル変数に転送$?する方法が問題です。リストした内容とは異なり、次の値のみが$?特別な意味を持ちます。

  • 126バイナリが存在していても実行できませんでした
  • 127指定されたバイナリは存在しません
  • 128終了ステータスは== 0でしたが、不特定の問題が存在します

さらに$?、シグナルによって中断されたプログラム用に予約されている、指定されていないシェルおよびプラットフォーム固有のコード範囲> 128があります。

  • Bourne Shell bashおよびksh88は128 +シグナル番号を使用します
  • ksh93は256 +信号番号を使用します。

他の値はシェル固有の$?値と区別される可能性があるため、問題はありません。

特に、値1および2は特別な条件には使用されませんが、組み込みコマンドがない場合でも同じように動作する可能性がある組み込みコマンドで使用される単なる終了コードです。だから、あなたが提供したbashスクリプトガイドへのポインタは、特定のコードが独自のスクリプトで避けるべき特別な値であるかどうかをコメントせずにbashで使用されるコードをリストするだけなので、良いマニュアルではないようです。

Bourne Shellの新しいバージョンは、プログラムが終了するwaitid()waitpid()を待つ代わりにwaitid()使用し(SVr4に1989を導入)、より優れたsyscallインターフェースを使用します(1980年にUNOSがすでに使用していたものと同様)。

新しいBourne Shellバージョンは、/ にある終了コードとは別の変数/ で終了理由をエンコードするためhttp://schillix.sourceforge.net/man/man1/bosh.1.htmlを参照してください。終了コードはオーバーロードされません特別な状態があり、 `waitid()を使用した結果、Bourne Shellは下位8ビットだけでなく、32ビットの終了コードをすべて返すようになりました。${.sh.code}${.sh.codename}${.sh.status}${.sh.termsig}

ところで:exit(256)これ$?は、古典的なシェルで0として解釈されることになりますので、Cプログラムやシェルスクリプトに注意しないでください。


2
ところで:私はwaitid()5月下旬頃にこのバグについてFreeBSDとLinuxカーネルに対するバグレポートを作成しました。FreeBSDの人々は20時間以内に問題を修正しましたが、Linuxの人々はバグを修正することに興味がありません。...そしてCygwinの人々は、彼らはLinux互換のバグによるバグだと言っています;-)
schily

2
この動作は、Single Unix Specificationで必要です。はい、32ビットの値がありますが、その値にはからの値の下位8ビットを含む8ビットのビットフィールドが含まれてい_exitます。あなたが言及しているFreeBSDバグレポートをリンクしてください。たぶん私はあなたが説明している問題を誤解しているでしょう。
Random832

2
OPは質問にbashのタグを付け、質問のテキストでBashに言及しました。BashはBourneから派生したシェルです。${.sh.}変数をサポートしていません。ただし、「ボーン派生」ではなく「ボーン」と言うのは事実です(ただし、ksh93は含めます)。
追って通知があるまで一時停止します。

2
この回答は、SVR4から派生したUnixの特定のバリアントに非常に固有のようです。V7にあったものを意味しない限り、 "the" Bourneシェルのようなものは存在しないことに留意して、移植可能なものとそうでないものについて明確にしてください。
zwol

4
それどころか、ここでの変動の範囲、特に歴史的変動を過小評価しているのはあなただと思います。/bin/shこれらの特別な終了コードをクロスプラットフォームで一貫して動作させるために信頼できるように聞こえますが、これは真実ではありません。(特定のシステムが「本物のBourneシェル」と言えるかどうかはにしません/bin/sh。これがPOSIXにないこと、そして「本物のUnixシステム」として引用するもののほとんどがそうではないことを知ることはさらに重要ですt /bin/shとにかくPOSIX準拠を提供します。)
zwol

6

シェルスクリプトの場合、シェルsysexist.h予約済みの終了コード(接頭辞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   
#...

また、次のもので生成できます。

#!/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; }'

あまり使用しませんが、使用するのはエラーコードを文字列形式に逆変換するシェル関数です。名前をつけましたexit2str。上記のexit.shジェネレータexit.sh.shに名前を付けたと仮定すると、のコードはexit2strexit2str.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
}"

PS1インタラクティブシェルでこれを使用して、各コマンドを実行した後、終了ステータスと文字列形式を確認できるようにします(既知の文字列形式がある場合)。

[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)$

これらを取得するには、exit2str関数のソースが必要です。

$ ./exit2str.sh.sh > exit2str.sh #Place this somewhere in your PATH

そして、それを使用して、~/.bashrc各コマンドプロンプトで終了コードを保存および翻訳し、プロンプトを表示します(PS1):

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

一部のプログラムが終了コードの規則に従っているかどうかを観察したり、終了コードの規則について学習したり、単に何が起こっているかをより簡単に確認したりするのに非常に便利です。しばらくそれを使用してきたので、多くのシステム指向のシェルスクリプトは規則に従っていると言えます。EX_USAGE他のコードはそれほどではありませんが、特に一般的です。私は時々慣習に従うようにしていますが、$S_EX_ANY怠laな人(私は1人)には常に(1)があります。


そのerrnoコードで報告されたエラーがエラー終了になる場合に使用するerrnoコードと終了コード間のマッピングのようなものがあるのだろうかと思います。妥当なマッピングを考え出す必要があるかもしれません。
PSkocik

1
うわー!私はそのような精巧な答えを期待していませんでした。さまざまなコマンドがどのように動作するかを確認する良い方法として、私は間違いなくそれを試してみます。ありがとう。
アンソニーG-モニカの正義

4

終了コードを文書化して、今から1年後に戻ってきてスクリプトを微調整する必要があるときに記憶しておく限り、大丈夫です。「予約済みの終了コード」という考え方は0、成功コードとして、また失敗コードとして他のものを使用するのが慣習的であると言うこと以外、実際にはもはや適用されません。


4

私が見つけた最高のリファレンスはこれでした:http : //tldp.org/LDP/abs/html/exitcodes.html

これによれば:

1 エラーの一般的なキャッチオールであり、ユーザー定義のエラーに使用されるのを常に見てきました。

2 構文エラーなど、シェルの組み込みの誤用です

質問に直接答えるには、予約済みのエラーコードを使用してスクリプトを正常に処理します。エラーコード= 1/2/3に基づいてエラーを処理すると想定したとおりに機能します。

ただし、予約済みのエラーコードを知っている人や使用している人に出会うと混乱する可能性があります。

使用可能な別のオプションは、エラーが発生した場合にエラーをエコーし​​てから終了することです。スクリプトがLinuxの「ニュースはありません」という慣習に従っており、成功するとエコーは何も起こりません。

if [ $? -ne 0 ];then
    echo "Error type"
    exit 1
fi

2

私が受け取った回答に基づいて(他のものよりも1つを選ぶのは困難でした)、Bashも使用する終了コードを使用して特定のタイプのエラーを示すことは有害はありません。ユーザースクリプトがこれらのエラーコードのいずれかで終了した場合、Bash(または他のUnixシェル)は特別なこと(例外ハンドラーの実行など)を行いません。

Advanced Bash-Scripting Guideの著者は、BSDが終了コードを標準化しようとすることに同意しているようで(sysexits.h)、ユーザーがシェルスクリプトを作成するとき、既に定義済みの終了コードと競合する終了コードを指定しないことを単に推奨していますつまり、カスタム終了コードを、64〜113の範囲で使用可能な50のステータスコードに制限します。

私はこのアイデア(および根拠)を高く評価していますが、著者が、アドバイスを無視することは有害ではないことをより明確に示していれば、スクリプトの利用者が127の引用例のようなエラーをチェックしている場合を除いて、 (command not found)。

関連するPOSIX仕様

POSIXが終了コードについて何を言っているのかを調査しましたが、POSIX仕様はAdvanced Bash-Scripting Guideの著者と一致しているようです。関連するPOSIX仕様を引用しました(私の強調):

コマンドの終了ステータス

各コマンドには、他のシェルコマンドの動作に影響を与える可能性のある終了ステータスがあります。このセクションでは、ユーティリティではないコマンドの終了ステータスについて説明します。標準ユーティリティの終了ステータスは、それぞれのセクションに記載されています。

コマンドが見つからない場合、終了ステータスは127になります。コマンド名が見つかったが実行可能なユーティリティではない場合、終了ステータスは126になります。シェルを使用せずにユーティリティを呼び出すアプリケーションは、これらの終了ステータス値を使用する必要があります同様のエラーを報告します。

ワードの展開またはリダイレクト中にコマンドが失敗した場合、その終了ステータスはゼロより大きくなります。

内部的に、コマンドがゼロ以外の終了ステータスで終了するかどうかを決定するために、シェルは、wait()関数WE​​XITSTATUSマクロ(System Interfacesボリュームで定義されている) POSIX.1-2008)。特別なパラメーター「?」を使用して終了ステータスを報告する場合、シェルは、利用可能な終了ステータスの完全な8ビットを報告します。シグナルを受信したために終了したコマンドの終了ステータスは、128を超えると報告されます。

exitユーティリティ

他のセクションで説明したように、特定の終了ステータス値は特別な用途のために予約されており、アプリケーションはこれらの目的にのみ使用する必要があります。

  • 126 –実行するファイルが見つかりましたが、実行可能なユーティリティではありませんでした。
  • 127 –実行するユーティリティが見つかりませんでした。
  • >128 –コマンドがシグナルによって中断されました。

さらに詳しい情報

何それの価値のために、私はリストのすべてが、1つ検証することができた特別な意味を持つの終了コードを。この終了コードの表は、詳細と、Bashリファレンスに記載されているエラーコードの生成方法の例を提供するので便利です。

128の終了ステータスを生成しようとしました

Bashバージョン3.2.25および4.2.46を使用して、128 Invalid argument to exitエラーをスローしようとしましたが、毎回255(Exit status out of range)を受け取りました。たとえば、exit 3.14159シェルスクリプトの一部として、またはインタラクティブな子シェルで実行された場合、シェルは次のコードで終了します255

$ exit 3.14159
exit
bash: exit: 3.14159: numeric argument required

さらに楽しくするために、単純なCプログラムを実行してみましたが、この場合、exit(3)関数は終了する前にfloatをint(この場合は3)に変換しただけのようです:

#include <stdlib.h>
main()
{
    exit(3.14159);
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.