次のようなスクリプトがあります。
#!/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