回答のまとめ(2020年1月2日)

回答のまとめ(2020年1月2日)

command_1ほとんどのLinuxガイドでは、「実行してから実行してからcommand_2実行する必要がありますcommand_3。」のようなページが含まれています。これらすべてのタスクを手動で実行するのに時間を無駄にしたくないので、スクリプトを作成することをお勧めします。

command_1
command_2
command_3

そして一度実行してみてください。しかし、多くの場合、いくつかのコマンドが失敗し、どのコマンドが失敗したのかわかりません。また、以前にエラーが発生した場合、通常、残りのコマンドはすべて意味がありません。したがって、より良いスクリプトは次のようになります。

   (command_1 && echo OK command_1 || (echo FAILED command_1; false) )
&& (command_2 && echo OK command_2 || (echo FAILED command_2; false) )
&& (command_3 && echo OK command_3 || (echo FAILED command_3; false) )
&& echo DONE 
|| echo FAILED

しかし、あまりにも多くの定型句コードを書く必要があり、各コマンドを3回繰り返す必要があり、いくつかの中括弧を誤って入力する可能性があります。最後のスクリプトが行った操作を実行するより便利な方法はありますか?特に:

  • コマンドを順番に実行
  • コマンドが失敗した場合
  • どのコマンドが失敗したかを記録してください(存在する場合)。
  • コマンドとの正常な対話を可能にします。すべての出力が印刷され、キーボード入力が許可されます(コマンドが何でも必要な場合)。

回答のまとめ(2020年1月2日)

ソリューションには2つのタイプがあります。

  • 修正なしでガイドのコピー/貼り付けコマンドを許可しますが、失敗したコマンドを印刷しません。したがって、失敗したコマンドが長い出力を生成する場合、どのコマンドが失敗したかを確認するには、多くの行を上にスクロールする必要があります。 (すべて人気のある回答)
  • 最後の行を印刷することは失敗しますが、コピーして貼り付けた後に引用符を追加するか(Johnの答え)、ステートメントを追加してリンクされたコマンドを別のコマンドにtry分割(Jasenの答え)してコマンドを変更する必要があります。

お前らは本当にすごいですが、私はこの話をしばらくしておきます。おそらく、誰かが両方の要件を満たすソリューション(最後の行に失敗したコマンドを印刷し、変更なしでコマンドをコピーして貼り付けることができる)を知っている可能性があります。

答え1

1つのオプションは、コマンドをbashスクリプトに入れますset -e

ゼロ以外の終了状態でコマンドが終了すると、スクリプトは早期に終了します。

Stack Overflowに関するこの質問も参照してください。https://stackoverflow.com/q/19622198/828193

エラーを印刷するには、次のものを使用できます。

trap 'do_something' ERR

do_somethingエラーを表示するために生成するコマンドはどこにありますか?

以下は、どのように動作するかを確認できるサンプルスクリプトです。

#!/bin/bash

set -e
trap 'echo "******* FAILED *******" 1>&2' ERR

echo 'Command that succeeds'   # this command works
ls non_existent_file           # this should fail
echo 'Unreachable command'     # and this is never called
                               # due to set -e

出力は次のとおりです。

$ ./test.sh 
Command that succeeds
ls: cannot access 'non_existent_file': No such file or directory
******* FAILED *******

さらに、前述のように@ジケ、パイプの終了状態は基本的にパイプの終了状態に設定されていることに注意してください。決定的なコマンドはその中にあります。これは、パイプラインの最後ではないコマンドが失敗してもブロックされないことを意味しますset -e。この問題を解決するには(心配されている場合)、次のものを使用できます。set -o pipefail


私が提案したようにこんにちはそして@モンティハード関数をハンドラとして使用すると、ネストされた参照を防ぐため、スクリプトを読みやすくします。とにかく、関数を使用しているので、set -e関数を完全に削除してハンドラexit 1で使用しました。これにより、一部の人が読みやすくなります。

#!/bin/bash

error_handler() {
  echo "******* FAILED *******" 1>&2
  exit 1
}

trap error_handler ERR

echo 'Command that succeeds'   # this command works
ls non_existent_file           # this should fail
echo 'Unreachable command'     # and this is never called
                               # due to the exit in the handler

スクリプトの終了状態は異なりますが、出力は上記と同じです。

答え2

珍しいソリューションが欲しいですか?インストールされている場合は、makeコマンドリストをMakefileforに入れてmake実行できます。追加の利点:エラーが発生したことを確認する必要はありません。make各コマンドの戻り値は自動的にチェックされます。 0 以外の場合、レシピはエラーで終了します。特定のコマンドのエラーを無視するには、そのコマンドをと組み合わせてください|| true

メイクファイルの例:

.PHONY: all
.SILENT:

all:
    echo "Started list of commands."
    true
    echo "Executing a command which will fail, but I want to ignore failure."
    false || true
    echo "Executing a command which will definitely fail."
    false
    echo "This code will not be reached."

注:上記のようにコマンドをインデントしますが、タブを使用する必要があります。

答え3

私はこれがbashでしか機能しないと思いますが、次のことを試すことができます。

set -o xtrace
set -o errexit

またはもう少し簡潔にしたい場合は、

set -ex

これは2つのことを行います。errexit(-e)はエラーが発生したときにスクリプトを中断し、xtrace(-x)はbashが実行される前に各コマンドを印刷するため、失敗した場合に実行中の内容を正確に知ることができます。

1つの欠点は、出力が複雑であることですが、同意する限り、最小限の作業しか必要としない非常に良いソリューションです。

  • これを行うときは、通常、次のものを含めることをお勧めしますset -o pipefail。それ以外を実行すると、foo | bar失敗はfoo自動的に無視されます。 (警告:パイプステートメントの「終了コード」を微妙に変更するため、他の人のスクリプトを修正する場合は注意して使用してください。また、時にはfoo実際に気にしない失敗があるので明らかにできません。pipefailこの場合に使用してください。)

答え4

failed=0
try(){
(( failed )) && return
"$@" 
ec=$?
if (( ec ))
then
  failed=$ec
  echo "failed($failed): $*" >&2
fi
}

try something args
try other-thing more args

if (( failed )) 
then
  echo "something went wrong: see above." >&2
  exit 1
fi

関連情報