ここで文字列にエラーがあるときにスクリプトが失敗するようにするにはどうすればよいですか?

ここで文字列にエラーがあるときにスクリプトが失敗するようにするにはどうすればよいですか?

次のようなスクリプトがあります。

#!/bin/bash
set -euo pipefail
IFS=$'\n\t'

while read -r l; do
    echo "${l}"
done <<< "$(cat input.txt)"

echo Success

このコマンドはcat input.txt私の問題を単純化するための一例です。

input.txtおかげで、スクリプトが存在しない場合はすぐに終了することを期待しましたset -euo pipefail。ただし、そうでない場合、スクリプトは次の出力で正常に終了します。

cat: input.txt: No such file or directory

Success

期待どおりにスクリプトを失敗させる方法はありますか?

答え1

私はこれがcat input.txt任意のコマンドのプレースホルダーであることを知っています。特にcat input.txt解決策は簡単です。入力リダイレクトを使用できますinput.txt

終了コードは、ここの文字列の次の拡張で使用できます。たとえば、

cat <<< "$(exit 3)$?"

出力 3. したがって、次のことができます。

#! /bin/bash -
set -o nounset -o pipefail -o errexit

unset -v ret
{
  [ "$ret" -eq 0 ] || exit "$ret"
  while IFS= read -r line || [ -n "$line" ]; do
    printf '%s\n' "$line" || exit
  done
} <<< "$(cat input.txt)"${ret[1+(ret=$?)]}"
echo success

これは少なくともbash 5.0で動作するようです。

あるいは、一時変数を使用することもできます。

#! /bin/bash -
set -o nounset -o pipefail -o errexit

output_minus_trailing_newlines=$(cat input.txt)
while IFS= read -r line || [ -n "$line" ]; do
  printf '%s\n' "$line" || exit
done <<< "$output_minus_trailing_newlines"

echo success

どちらの場合も、コマンドが失敗した場合、その出力は処理されません。

lastpipeループがサブシェルで実行されたくない場合は、パイプとオプションを使用することもできます。

#! /bin/bash -
set -o nounset -o pipefail -o errexit
shopt -s lastpipe

cat input.txt |
  while IFS= read -r line || [ -n "$line" ]; do
    printf '%s\n' "$line" || exit
  done

echo success

これにより出力が処理されます。errexit-e)との組み合わせによってコマンドが失敗した場合、パイプが終了した後にのみシェルが終了しますpipefail。この場合、末尾の空行が維持されます。

同じ手順に置き換えてください。

#! /bin/bash -
set -o nounset -o pipefail -o errexit

{
  pid="$!"
  while IFS= read -r line || [ -n "$line" ]; do
    printf '%s\n' "$line" || exit
  done
  wait "$pid"
} < <(cmd input.txt)

echo success

あるいは、一時ファイルを使用することもできます(ここにはドキュメントがあり、ここには一時ファイルを使用してbashで実装された文字列があります)。

#! /bin/bash -
set -o nounset -o pipefail -o errexit
tmp=$(mktemp)
{
  rm -f -- "$tmp"
  cat input.txt >&3
  exec 3>&-

  while IFS= read -r line || [ -n "$line" ]; do
    printf '%s\n' "$line" || exit
  done
  wait "$pid"
} 3> "$tmp" < "$tmp"

echo success

これにより、コマンドが失敗し、出力をメモリに複数回保存しないと、出力は処理されず、後続の空行が維持されます。

とにかくぜひご覧くださいシェルループを使用してテキストを処理するのはなぜ悪い習慣と見なされますか?

答え2

このように:

#!/bin/bash
set -euo pipefail
IFS=$'\n\t'

[[ -s input.txt ]]
while read -r l; do
    echo "${l}"
done <<< "$(cat input.txt)"

echo Success

関連情報