時間コマンドと同様に、コマンドのログ終了コード


10

を使用して

time sleep 1

利回り:

$ time sleep 1

real    0m1.005s
user    0m0.001s
sys     0m0.001s

終了コードsleepや実行したいコマンドを出力するために使用できるコマンドはありますか?

好きなもの:

$ log-exit-code sleep 1

おそらくこれで十分ですか?

sleep 1 && echo "$?"

3
いいえ、sleep 1 && echo $?スリーパーセルのコードはゼロの場合にのみ出力されます...
桂佐藤

ああ笑あなたは正しいです
アレクサンダーミルズ

1
sleep 1 && echo "$?" || echo "$?"
jesse_b 2017

2
これまで他のすべてのものが元の戻り値を破棄するので、私は答えを追加しましたが、imoは悪いです(これらの答えの後、$?は常に0です。元の戻り値を表示し、それを表示した後に返します。したがって、オブザーバーに表示するだけですが、残りのスクリプトでは「非表示」にして、「$?」を使用して重要なコマンドが返されるかどうかを確認することはできません0または他の何か...)
Olivier Dulac

1
また、以下のすべてのソリューションは、この機能を簡単に実装できます。Kusalanandaの例のためだけに、次のようになりますEXIT_CODE=$(tellexit command)
jesse_b

回答:


4

これまでの私のテストでは、これはうまくいきました:

command && echo "$?" || echo "$?"

成功または失敗した場合に終了コードをエコーするように指示するだけです。

佐藤が下で指摘したように、これは基本的に以下と同じです:

command; echo "$?"

および/またはコマンドを価値のあるものにすることができる1つのことは次のようなものです:

command && echo "Success! Exit Code: $?" || echo "Failure! Exit Code: $?"

Olivierの懸念であるように、スクリプトが終了コードを処理する必要がある場合、それは問題ではありません。スクリプトは次のようになります。

command
case "$?" in; 
    0) echo "Command exited with: 0"
       <command if good>
       ;;
    1) echo "Command exited with: 1"
        <command if bad>
        ;;
    255) echo "Command exited with: 255"  # for ssh maybe?
         <command if 255>
         ;;
    *) echo "Command exited with: >2"
        <command for other exit code>
        ;;
esac

13
えっと、それだけではcommand; echo $?どうですか?
桂佐藤

4
違いますね。 command; echo $?はるかに良い答えです。
jesse_b 2017

ええと、これは最良の選択肢のようです。誰かが同意しない場合は、lmk
Alexander Mills

@AlexanderMillsあなただけがあなたの質問に最も適切な答えを知っています:-)
クサラナンダ

1
私はこれが嫌いです:これは常に成功するcommand ; echo $?ため、終了コードを変更し、その後echo $?、$の新しい値を変更しますか?は現在0です。この元の戻りコードが後で役立つ場合に備えて、表示した後でも元の戻りコードを保持する方法があります(たとえば、スクリプトで、続行方法を決定する前に戻りコードを確認することが重要です)。いくつかの解決策の1つについて私の回答を参照してください(ここでのほとんどの回答は同じ方法で変更できます)
Olivier Dulac

11

cmd && echo "$?"これは、必然的にゼロechoのみを出力するため、機能しません(前のコマンドが正常に完了したときにのみ実行されます)。

ここにあなたのための短いシェル関数があります:

tellexit () {
    "$@"

    local err="$?"
    printf 'exit code\t%d\n' "$err" >/dev/tty
    return "$err"
}

これは、コマンドと同様の方法で、指定されたコマンドの終了コードを出力timeします。

$ tellexit echo "hello world"
hello world
exit code       0

$ tellexit false
exit code       1

リダイレクトすることによって、printf/dev/tty機能して、我々はまだ使用することができtellexit、当社の標準出力またはエラーストリームにジャンクを取得せずにリダイレクトして:

$ tellexit bash -c 'echo hello; echo world >&2' >out 2>err
exit code       0
$ cat out
hello
$ cat err
world

終了コードを変数に保存することにより、呼び出し元に終了コードを返すことができます。

$ tellexit false || echo 'failed'
exit code       1
failed

同じ関数のより洗練されたバージョンは、終了コードが128より大きい場合にコマンドを強制終了したシグナルも出力します(つまり、シグナルが原因で終了したことを意味します)。

tellexit () {
    "$@"

    local err="$?"

    if [ "$err" -gt 128 ]; then
        printf 'exit code\t%d (%s)\n' "$err" "$(kill -l "$err")" >/dev/tty
    else
        printf 'exit code\t%d\n' "$err" >/dev/tty
    fi

    return "$err"
}

テスト:

$ tellexit sh -c 'kill $$'
exit code       143 (TERM)

$ tellexit sh -c 'kill -9 $$'
Killed
exit code       137 (KILL)

(これにlocalash/ pdksh/ bash/ zshが必要ですがtypeset、他のいくつかのシェルでも理解できるように変更することもできます。)


クールに、tellexitをどのように使用し、実際にそれを表示しますか
Alexander Mills

1
@AlexanderMills更新を参照してください。
クサラナンダ

2
元の戻り値を保持し、最終的にそれを返す必要があります。
Olivier Dulac

@OlivierDulac本当に良い観察です!修正します。
クサラナンダ

7

シェルラッパー関数を使用してください。おそらく別の名前です。

$ exito() { "$@"; echo $?; }
$ exito true
0
$ exito false
1
$ exito echo "test test"      
test test
0
$ 

(もちろん、これは標準出力を破壊するため、tty@ Kusalanandaで示されているように使用するか、インタラクティブコンテキストの外では使用しないでください。)

一部のシェルは、移植性のない領域に移り、最後のコマンドだけでなく、パイプライン内のすべてのコマンドのステータスを報告できます。

% TRAPZERR() { print >/dev/tty $pipestatus }
% perl -e 'exit 42' | perl -e 'exit 99'
42 99
% false | perl -e 'exit 42' | perl -e 'exit 99'
1 42 99
% perl -e 'exit 42' | perl -e 'exit 99' | true
% 

TRAPZERR それ以外の場合は、エラーが発生しないときに(「ニュースはありません」というプリンシパルで)起動しません。


1
元の戻り値を保持し、最終的にそれを返す必要があります。単純なもの(「-」で始まらないなど)の表示(printf、echo)は常に機能し、その後に「$?」が表示されます。ディスプレイが機能したため、現在の値は「0」です
Olivier Dulac

7

GNUにtimeはこれのためのオプションがあります:

time -f %x sleep 1
0

シグナル2で強制終了されない限り、終了コード1を通過します。

$ /usr/bin/time -f %x sleep 1; echo $?
0
0

$ /usr/bin/time -f %x sleep 2x; echo $?
sleep: invalid time interval ‘2x’
Try 'sleep --help' for more information.
1
1

$ /usr/bin/time -f %x sleep 60s; echo $? # Press ^C before a minute elapses
0
2

シグナルキルの状況を確認/処理する場合-vは、文字列のstderrを渡してgrepしますCommand terminated by signal


1終了コードが通過することに注意してくれたOlivier Dulacに感謝します。
2また、キル信号の終了コードが通過しないことを指摘してくれたStéphaneChazelasに感謝します。


1
インタレスト。GNUのみですが、興味深いのは、残りのコードが(おそらく)元の戻りコードを保持するためです(つまり、他の回答のように「0」に置き換えられません)
Olivier Dulac

2
0ただし、コマンドが強制終了された場合は戻ります。
ステファンChazelas

2
@bishop、私はそれらの場合0が理想的ではないことに同意します。ただし、コマンドがシグナルによって強制終了された場合に何を報告するかについては、一般的な合意がないことに注意してください。128+シグナル番号を返すことは、いくつかのシェルで使用されています。あなたはまた、のようなものを見256+signumたり384+signum、あるいはsignum/256またはsignum/256+0.5コアが生成された場合(実際にによって返されるステータスwaitpid()256で割った値)。テキスト表現を提供するものもあります(sigintまたはsigquit+core)。それでも、おそらく同意するgnu.coreutils.bugsニュースグループで議論する価値があります。
ステファンChazelas

3
(実際timeの一部ではないcoreutils、それはだ、独自にパッケージ独自のバグのメーリングリストで(bug-time@gnu.org))
ステファンChazelas

1
@ bishop、system()Brian Kernighan(kin awk)によって現在も維持されている従来のawksのようなもの、またはnawkSolarisの、awkそれから派生したFreeBSDの。コアダンプサイズに制限がない場合にawk 'BEGIN{print system("kill -s ABRT $$")}'出力します0.523438。挙動はgawkして行動を変化させて、最近変更された--traditional--posix(もの戻り値を参照してくださいclose(cmd)より多くのバリエーションのために)
ステファンChazelasを

2

私は他のすべての答えが嫌いです(私は彼らの賢さのために多くが好きですが):彼らは終了コードを表示しますが、それらも変更します。(表示部分はtrueであるため、戻りコードは0になります)

ここに変更されたバージョンがあります:

do_and_tell () {
   "$@"
   returncode="$?"
   printf "Execution of : \n%s\n yelded a return code of: \n%s\n" "$*" "$returncode"
   return $returncode  # the interresting addition... keeps the commands return value.
}

## usage:

prompt$ do_and_tell true
Execution of : 
true
 yelded a return code of: 
0

prompt$ echo $?
0

prompt$ do_and_tell false
Execution of : 
false
 yelded a return code of: 
1

prompt$ echo $?
1

実際、@ bishopの回答は、この値を維持する唯一の回答ですが、time常に利用できるわけではないGNUのバージョンに依存しています。
Olivier Dulac 2017

2

もう少し堅牢にするために、終了ステータスを別のFDに送信して、stdout / stderrとは独立して処理できるようにします。

exit_status() {
    "$@"
    rv=$?
    echo >&3 "$rv"
    return "$rv"
}

# exit status and stdout to stdout:
> exit_status echo "hello" 3>&1
hello
0

# exit_status to stdout, stdout to /dev/null
> exit_status echo "hello" 3>&1 1>/dev/null
0
> exit_status ehco "hello" 3>&1 1>/dev/null
ehco: command not found
127

# exit_status to stdout, stdout and stderr to /dev/null
> exit_status ehco "hello" 3>&1 &>/dev/null
127

あなたはFD3で何かをする必要があることに注意してください、そうでなければそれは言うでしょうBad file descriptor

これは、他のプログラムに複数のFDをリッスンさせることによって、これらの出力を別のプログラムの入力に提供するようにさらに構成できます。これを90年代前半のKibanaの一種として使用できます。


元の戻り値を保持し、最終的にそれを返す必要があります。単純なもの(「-」で始まらないなど)の表示(printf、echo)は常に機能し、その後に「$?」が表示されます。ディスプレイが機能したため、現在の値は「0」です
Olivier Dulac

1

他のすべての(非常に興味深い)回答は、OPが尋ねた正確な質問に対応しますが、実際には、物事は通常より簡単です。終了ステータスを変数(コマンドの直後)に保存し、任意の方法でレポートまたはログを記録するだけです。たとえば、/ dev / stderrに出力するか、テキストメッセージとしてログファイルに書き込み/追加します。また、後続のコードの意思決定/フロー制御で使用するために保持されます。

関数内にある場合:

function some-function () {
    local rc ## to avoid side effects
    ...
    some-command ## run any command
    rc=$?  ## save the return status in a variable
    echo "some-command returned $rc" ## optionally tell the user about it
    ...
    return $rc  ## at the end, if appropriate
}

スクリプト内に直接ある場合:

some-command ## run any command
rc=$?  ## save the return status in a variable
echo "some-command returned $rc" ## optionally tell the user about it
...
exit $rc    ## at the end to tell any calling script about it, if appropriate

(これは正確な質問に答えるものではないため、ダッキングとカバリングです。)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.