web-dev-qa-db-ja.com

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

最近、高度なBashスクリプトガイドからこの特別な意味を持つ Exit Codes With Special Meanings のリストを見つけました。彼らはこれらのコードをreservedとして参照し、以下を推奨します。

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

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

  • 0-成功
  • 1-正しくないホスト名
  • 2-指定された引数が無効です
  • 3-ユーザー権限が不十分

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

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

Bashのドキュメントに裏付けとなるアドバイスはありませんでした。 Exit Status のセクションには、Bashで使用される終了コードがリストされていますが、これらのいずれもreservedまたは独自のスクリプト/プログラムでそれらを使用しないよう警告します。

16

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

  • bSDには _sysexits.h_ があり、64以上の値の意味を定義します。

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

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

  • Windowsでは、 NTSTATUS コード(32ビットの数値空間全体に不便に散在している)が終了コードとして使用される可能性があり、特に、プロセスが致命的な原因で終了したことを示すコード誤動作(例:_STATUS_STACK_OVERFLOW_)。

これらの規則のいずれかに従う特定のプログラムを当てにすることはできません。唯一のreliableルールは、終了コード0が成功で、それ以外は何らかの失敗であるということです。 (C89の_EXIT_SUCCESS_はnotの値がゼロであることが保証されていることに注意してください。ただし、exit(0)は同じように動作する必要があります値が同じでなくてもexit(EXIT_SUCCESS)になります。)

10
zwol

いいえ終了コードには特別な意味がありますが、_$?_の値には特別な意味がある場合があります。

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

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

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

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

その他の値は、シェルの特殊な_$?_値と区別される可能性があるため、問題にはなりません。

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

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

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

ところで:Cプログラムやシェルスクリプトからexit(256)や類似のものに注意しないでください。これにより、クラシックシェルでは_$?_が0として解釈されます。

11
schily

シェルスクリプトの場合、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に名前を付けたとすると、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
}"

これを対話型シェルの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は特に一般的ですが、他のコードではそれほど多くありません。怠惰な人々(私は1人です)には常に$S_EX_ANY(1)がありますが、私は時々規則に従うようにしています。

6
PSkocik

私が見つけた最高のリファレンスはこれでした: 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
4
Centimane

私が受け取った回答(他のものを選択するのは困難でした)に基づいて、特定のタイプのエラーを示すことは有害ではありません 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になります。なしでユーティリティを呼び出すアプリケーションシェルを使用する場合、これらの終了ステータス値を使用して同様のエラーを報告する必要があります。

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

内部的には、コマンドがゼロ以外の終了ステータスで終了するかどうかを決定するために、シェルは、(システムインターフェースのボリュームで定義されている)wait()関数WE​​XITSTATUSマクロと同等のコマンドによって取得されたステータス値全体を認識します。 POSIX.1-2008)。特別なパラメーター '?'で終了ステータスを報告するとき、シェルは利用可能な終了ステータスの完全な8ビットを報告します。シグナルを受信したために終了したコマンドの終了ステータスは、128より大きいと報告されます。

exitユーティリティ

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

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

さらに詳しい情報

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

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

Bashバージョン3.2.25および4.2.46を使用して、_128 Invalid argument to exit_エラーをスローしようとしましたが、毎回255(範囲外の終了ステータス)を受け取りました。たとえば、_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);
}
_
2