web-dev-qa-db-ja.com

失敗した猫が1を返すのに、他の失敗した猫は2を返すのはなぜですか?

検討してください:(Linux/BASHを使用して、UNIX Properについては不明)

存在しないファイルを主張するときに2エラーが発生することを期待しています...

grep "i am here" real-file

# Returns: 0 (via: echo $?)

grep "i am not here" real-file

# Returns: 1

grep "i am not here" not-a-file

# Returns: 2 (No such file or directory)

ls real-files

# Returns: 0

ls not-files

# Returns: 2 (No such file or directory)

...それらは理にかなっていますが...

cat real-files

# Returns: 0

cat not-files

# Returns: 1 (No such file or directory)

...「そのようなファイルまたはディレクトリはありません」は、終了ステータス2のSTDERRではないでしょうか?

ステータス2にはgrepおよびls非ファイルが付属していますが、catは1を返し、同じエラーメッセージが表示されます。

grepには3つの結果(上記のそれぞれ)があることを認識していますが、lsにはcatと同様に2つしかないと思います。したがって、catではそうではないため、2つの考えられる結果がlsの理由にならない可能性があります。

これはBASHコードの問題ですか? LinusとRichardに電話する必要がありますか?これが正しい場合は、その理由を教えてください。


回答を受け入れた後、これはLinux/BASHであり、UNIXではないので、元の質問を拡張した回答が適切です。

12
Jesse Steele

パーツのいくつかを下から上に向かって扱い、重要でないパーツを最初に削除してみましょう。

これはBASHコードの問題ですか?

いいえ、catは完全に独立したバイナリアプリケーションであり、bashとは無関係です。一部のシェル構成では、 Stephane Chazelas で指摘されているように、catを組み込みにすることができますが、アプリケーションの戻りステータスは、そのアプリケーションが関連しているかどうかとはまったく異なりますシェルにするかしないか。

LinusとRichardに電話する必要がありますか?これが正しい場合は、その理由を教えてください。

いいえ、問題ありません。LinusとRichardはここではまったく関係ありません。まあ、修正:彼らがいつかexit()とerrnoが完全に関連している必要があることを宣言しない限り、何らかの奇妙な理由で、私たちは彼らのすべての技術的な決定に従う必要があります。


POSIX仕様には「このゼロ以外の終了ステータスはこれとそれを意味する」という明示的な制限または割り当てがないため、両方のアプリケーションが異なる終了ステータスを返すことはまったく問題ありません。

終了syscallのPOSIXドキュメント 状態:

ステータスの値は0、EXIT_SUCCESS、EXIT_FAILURE、またはその他の値のいずれかですが、待機中の親プロセスが使用できるのは最下位8ビット(つまり、ステータスと0377)のみです。

これは、ステータス0のみに意味が割り当てられていることを意味し、これは stdlib.h 仕様で指定されているようにEXIT_SUCCESSに割り当てられます。しかし、これはPOSIX仕様ですが、Linux仕様はどのように比較されますか?まあ、それはほぼ同じです:Linux exit(3) マニュアルでは可能な値が何であるかも指定されていません。

エラーが発生した場合でも、アプリケーションが特定の値で終了する必要はないの意味で、「あるかもしれない」ではなく、「あるかもしれない」と書かれていることにも注意してください。アプリケーションでエラーまたは障害が発生しても、終了時に0が返されます。

ただし、POSIX仕様各ポータブルアプリケーションの場合は、[終了ステータス]セクションを指定します。これは各アプリケーションに固有です。繰り返しますが、成功の場合は0以外、何でもゼロ以外のパターンはありません。そうしないと。たとえば、 POSIX cat specs には以下が必要です。

_The following exit values shall be returned:

0    All input files were output successfully.

>0   An error occurred.
_

grep の場合:

_The following exit values shall be returned:

 0    One or more lines were selected.
 1    No lines were selected.
>1    An error occurred.

_

Linuxのコンテキスト内では、 cat(1) はこれらのステータス値を明示的に示しませんが、 GNUのドキュメントは です。 grep(1) 手動で2の終了コードを使用して言及しているが、POSIX実装ではエラーに対してゼロより大きい条件のみが必要であることを認め、「...移植性のために、 2との厳密な等価ではなく、この一般的な条件をテストするロジック。」


いくつかのケースでは、exit()ステータス値が errno 値に等しいという仮定があることに言及する価値があります。今のところ、POSIXが必要とすることを示唆するドキュメントやリファレンスは見つかりませんでした。実際、それは逆です。 POSIX exit specおよびLinux exit(3) manページしない終了ステータスが何らかの形でerrnoと一致する必要があることを明示的に述べていることに注意してください。したがって、GNU grepの2の戻り値がENOENTエラー値2と一致するという事実は、まったくの偶然です。

実際、 errno.h を考慮すると、特定の整数値を割り当てる必要はなく、実装に依存します。したがって、ENOENTを整数2として扱うUnixのような実装が存在する可能性は十分あります。ただし、終了ステータスとerrnoは別のものであるため、これはまったく無関係です。

結論

catgrepとは異なる終了コードを返すという事実は適切であり、これらのアプリケーションの仕様と一致しています。終了コードの意味は固定されておらず、個々のアプリケーションに依存しています(それがcatまたはgrepのようなPOSIXアプリケーションである場合は例外です)。

引用するには GNU OSのドキュメント :「最も一般的な規則は、成功の場合は単に0、失敗の場合は1です。比較を実行するプログラムは異なる規則を使用します。ステータス1を使用して不一致を示し、ステータス2を使用して比較できないことを示します。既存の規約が意味をなす場合、プログラムは既存の規約に従う必要があります。」

19

GNU coreutils documentation of cat

ゼロの終了ステータスは成功を示し、ゼロ以外の値は失敗を示します。

...ゼロ以外の終了ステータスは失敗を示し、それ以上でもそれ以下でもありません。

grepのmanページ:

通常、終了ステータスは、行が選択されている場合は0、行が選択されていない場合は1、エラーが発生した場合は2です。ただし、-qまたは--quietまたは--silentが使用され、行が選択されている場合、エラーが発生しても終了ステータスは0です。

そしてlsのmanページ:

終了ステータス:
0 OK、
1軽微な問題(サブディレクトリにアクセスできないなど)の場合、
2重大な問題(例:コマンドライン引数にアクセスできない)の場合。

結果はドキュメントと一致しています。

18
Freddy

プログラムの終了ステータスはいくつかの規則に従う必要があり、これらの規則の他に一般的な規則があります。これらの規則はいずれも、プログラムを終了させる原因となった低レベルのエラーに関連していません。ファイルが存在しないために終了することを決定した特定のエラーコード、およびファイルへのアクセス許可が拒否されたために終了することを決定した場合は別のエラーコード、および別のエラーコードで終了するプログラムを作成することが可能です。パスのディレクトリコンポーネントが非ディレクトリであることが判明した場合などですが、それは非常にまれであり、配置が困難です。

プログラムの終了ステータスは整数値です。 POSIXシステム では、この値のタイプはintであり、通常は-2の範囲です。31 2に31+1。ただし、この範囲のほとんどは、いくつかの理由で実際には使用できません。何よりもまず、歴史的な理由により、プログラムがその子の終了ステータスを監視できるようにするほとんどのインターフェイスは、終了ステータスの下位8ビットのみを返します。これは0〜255の値です。これにはシステム関数が含まれます wait および waitpid ¹および シェルの終了ステータス ²。したがって、ほとんどすべての意図と目的で、終了ステータスは8ビット値です。

値0は成功として扱われ、他のすべての値は失敗として扱われます。これは、シェルの場合です。ここで、 ブール演算子ifおよびwhile構文 と、true /の概念を含むその他すべてのものです。 falseは、終了ステータス0をtrue、その他のステータスをfalseと見なします。これは make の場合にも当てはまります。ゼロ以外の終了ステータスでは、ビルドがエラーメッセージとエラーステータスで停止します。あなたはそれが慣習であるかどうかを疑うことができます(プログラムの作成者は技術的に必要なステータスを返すことができ、「成功」と「失敗」はとにかく正式に定義されていないため)、実際には、ステータス0で終了するプログラムは成功したと見なされ、別のステータス(1〜255)で終了するプログラムは失敗したと見なされます。

特に範囲を制限するシェルのその他の機能は、 シェルの終了ステータス (_$?_で確認)が他の情報をエンコードすることです。

  • 126は、コマンド名が実行可能ではない既存のファイルであることを示します。
  • 127は、コマンド名が見つからなかったことを示します。
  • 128 +[〜#〜] n [〜#〜]伝統的に(そして今日でもほとんどのシェルで)、コマンドはシグナル[〜#〜] n [〜#〜]。いくつかのシェルは、常に128を超える、異なる範囲を使用します。

したがって、実際には、プログラムは125を超える終了ステータスを有効に使用できません。これにより、1〜125の値がさまざまなエラーを表します。

やや広まっていますが、より大きな値は「悪い」失敗として扱われるという普遍的な慣習にはほど遠いです。特に、grepなどの検索コマンドの場合、1は「見つかりません」を示し、2以上は検索を妨げるエラーを示します(たとえば、ファイルが見つかりませんが、検索文字列が含まれていないのではなく、ファイルが見つかりません)。 )。同様に、 cmpdiff などの比較コマンドは、ステータス0で終了し、「同一ファイル」を意味し、1は「異なる」を意味しますファイル」および「エラーのために比較を完了できなかった」を意味する2以上。

sendmail やその他のメール関連プログラム( _sysexits.h_ で定義された値)、 rsynccurlwget

エラーコードの最も一般的な規則は、成功の場合は0、失敗の場合は1です。 CおよびC++プログラミング言語は、特定の値を選択する特別な理由がなく、_EXIT_FAILURE_が1である場合に、失敗を報告するために使用する終了ステータスコードとして _EXIT_FAILURE_ を定義しますほとんどのシステム。

「そのようなファイルまたはディレクトリはありません」、「アクセスが拒否されました」、「ディレクトリではありません」などのエラーは、内部的に数値エンコーディングを持っています。それらは errno 値です。何が問題だったかを示すためにシステム関数によって返されます。 Errno値は、プログラムの終了ステータスとしては一般的に役に立ちません。それらは、特定のプログラムにとって何が意味するかではなく、何がうまくいかなかったかの特徴点をエンコードします。たとえば、 wget の終了ステータスは、「オプションの解析エラー」(通常、基本的なシステムエラーなし)、「ローカル入出力エラー」(基本的なシステムエラーに関係なく)、「ネットワーク障害」(これは、ローカルI/Oと同じシステムエラーを共有します)など。ネットワークのエラーまたはローカルファイルのエラーが原因でwgetが失敗したかどうかを知ることは、パイプの破損(パイプへの書き込み)が原因で失敗したかどうかを知るよりも有用です。 、またはネットワークソケットでの接続を閉じましたか?)またはアクセス許可エラー(構成ファイルを読み取れないか、ローカルポリシーによってネットワークアクセスが拒否されましたか?).

戻り状況がerrno値に従うことは、あまり一般的ではありません。これは、Perlの die 関数が機能する方法が原因で、特にPerlスクリプトで発生します。ただし、上で述べたように、errno値が情報の最も有用な部分であることがほとんどないためだけでなく、errno値が1〜125の範囲になる理由がないため、これは悪い考えです。幸い、私はerrno値が1〜255の範囲外にあるシステムを知りません。そのため、少なくともexit(errno)(またはPerlのdie)は、上記のように256の倍数で成功します。しかし Linuxの場合 、たとえば、それらは126に達し、exit(errno)で_errno == ERFKILL_を使用して終了したプログラム(「RF-killのため操作は不可能」) SIGILL(違法な命令)で死亡したプログラムのシェルと区別がつきません。

¹ waitid _infop->si_status_を介して完全なint値へのアクセスを許可します。
² _$?_またはその他の方法で。たとえば、_exit256_がexit(256)で終了するプログラムである場合、シェルコマンド_if exit256; then echo "exited with 0"; fi_は「0で終了」を出力します。

成功

一般に、 POSIX仕様に記載 として、終了ステータス0のみが次のように定義されます。

引数statusの値がゼロ(またはゼロである必要があるEXIT_SUCCESS)は、通常、正常終了を示します。これは、ISO C標準のexit()の仕様に対応しています。規約の後には、makeやさまざまなシェルなどのユーティリティが続き、子プロセスからのゼロのステータスを成功と解釈します。このため、アプリケーションは、異常終了したときにexit(0)または_exit(0)を呼び出さないでください。たとえば、シグナルをキャッチする関数です。

POSIX仕様で指定されているということは、すべてのUNIXに適用する必要があることを意味し、おそらくLinuxもその仕様に従うでしょう。

Failure

対照的に、終了ステータスの他の値はすべて失敗です。

それは言うことができるすべてであり、終了ステータスが持つことができるすべての意味です。

終了ステータス値と他の値のリストの間には何の関係もありません。C関数によって通常設定されるerrnoさえもです。 ENOENTがたまたま2であることは、2の終了ステータスとは関係がなく、そうであるとは期待できません。

[(ファイルを機能させる必要がない)などのユーティリティがファイルのエラーを報告するのはなぜですか?

実際、[のPOSIX仕様はこれのみを定義しています。

次の終了値が返されます。

0        expression evaluated to true.

1        expression evaluated to false or expression was missing.

>1       An error occurred.

そして、man bashの例として:

すべての組み込み関数は、不正な使用を示すために2の終了ステータスを返します。
一般に無効なオプションまたは欠落している引数。

これはファイルの読み取りとは関係ありません。

シェル

これはBASHコードの問題ですか?

いいえ、SUCCESSが0で示される限り、まったくありません。

LinusとRichardに電話する必要がありますか?

なぜそれらを気にするのですか?

どうして?

これが正しい場合は、その理由を教えてください。

私が提供できる唯一の理由( "合理的な"要件であると判断した開発者に尋ねなかった)は、古いことわざと同じです。

送信する内容は慎重に、受け入れる内容は自由に

エラーの報告方法(およびエラー)について、各アプリケーション開発者に最大の柔軟性を残します。

UNIX(つまりMacの場合)は同じことを行うのですか?

はい、MacをUNIXとして認定したい場合は、POSIX仕様に従う必要があります。


編集:

必要なものが数字の場合、freeBSDでは:

grep "i am here"     real--file; echo "$?"     # ==> 0
grep "i am not here" real--file; echo "$?"     # ==> 1
grep "i am here"     not-a-file; echo "$?"     # ==> 2
cat  real--file                                # ==> 0
cat  not-a-file                                # ==> 1
3
Isaac

システムに応じて、catは組み込みのシェルまたは個別のバイナリの場合があります。それがどれであるかを確認するには、実行できます

$ command -V cat

また、GNU catの動作は、POSIX cat(1)

終了ステータス

次の終了値が返されます。


すべての入力ファイルが正常に出力されました。

> 0
エラーが発生しました。

Errnoは終了コードの下位8ビット(POSIXが渡すことを要求するもの)の範囲にある必要がないため、errnoと終了ステータスの関係は単なる偶然です。

ただし、POSIX '01準拠のSunOS 5.10はここでは2を返します(XPG3とPOSIX '01の間の標準は明らかにこのツールの動作を変更していません)。

$ PATH=`getconf -v POSIX.1-2001 PATH`
$ export PATH
$ command -v cat
/usr/bin/cat
$ cat nosuchfile
cat: cannot open nosuchfile
$ echo $?
2

実際、SolarisでもENOENTになります。

$ grep ENOENT /usr/include/sys/errno.h
#define ENOENT  2       /* No such file or directory            */

ただし、マンページには> 2のみが記載されています。

1
larkey