SOに関するQ&A から取得した次の方法を使用して、ファイル名を配列に解析するスクリプトがあります。
unset ARGS
ARGID="1"
while IFS= read -r -d $'\0' FILE; do
ARGS[ARGID++]="$FILE"
done < <(find "$@" -type f -name '*.txt' -print0)
これはうまく機能し、すべてのタイプのファイル名のバリエーションを完全に処理します。ただし、存在しないファイルをスクリプトに渡す場合があります。例:
$ findscript.sh existingfolder nonexistingfolder
find: `nonexistingfile': No such file or directory
...
通常の状況では、スクリプトにRET=$?
のような終了コードをキャプチャさせ、それを使用して続行する方法を決定します。これは、上記のプロセス置換では機能しないようです。
このような場合の正しい手順は何ですか?リターンコードをキャプチャするにはどうすればよいですか?置換されたプロセスで問題が発生したかどうかを判断するためのより適切な方法は他にありますか?
Stdoutにリターンをエコーすることで、サブシェル化されたプロセスからリターンを簡単に取得できます。同じことがプロセス置換にも当てはまります。
while IFS= read -r -d $'\0' FILE ||
! return=$FILE
do ARGS[ARGID++]="$FILE"
done < <(find . -type f -print0; printf "$?")
それを実行すると、最後の行(または場合によっては\0
区切りセクション)がfind
の戻りステータスになります。 read
は、EOF-を取得すると、1を返します。したがって、$return
が$FILE
に設定されるのは、の最後のビットだけです。で読み込まれた情報。
私はprintf
を使用して、余分な\n
ewlineを追加しないようにしています。これは、read
でも定期的に実行されるため重要です-\0
NULで区切られていないもの-読み込んだばかりのデータが\n
ewlineで終わっていない場合、0以外を返します。したがって、最後の行が\n
ewlineで終わっていない場合、readin変数の最後の値が戻り値になります。
上記のコマンドを実行してから:
echo "$return"
0
そして、プロセス置換部分を変更すると...
...
done < <(! find . -type f -print0; printf "$?")
echo "$return"
1
より簡単なデモンストレーション:
printf \\n%s list of lines printed to pipe |
while read v || ! echo "$v"
do :; done
pipe
そして実際には、必要なリターンがプロセス置換内からstdoutに書き込む最後のもの、またはこの方法で読み取るサブシェルプロセスであれば、$FILE
は常にリターンになります完了したときに必要なステータス。したがって、|| ! return=...
の部分は厳密には必要ありません。これは、概念を示すためにのみ使用されます。
プロセス置換のプロセスは非同期です。シェルはそれらを起動し、その後、それらがいつ死ぬかを検出する方法を提供しません。そのため、終了ステータスを取得することはできません。
終了ステータスをファイルに書き込むことはできますが、ファイルがいつ書き込まれたかわからないため、これは一般に扱いにくいものです。ここでは、ファイルはループの終了直後に書き込まれるため、それを待つのが妥当です。
… < <(find …; echo $? >find.status.tmp; mv find.status.tmp find.status)
while ! [ -e find.status ]; do sleep 1; done
find_status=$(cat find.status; rm find.status)
別のアプローチは、名前付きパイプとバックグラウンドプロセス(wait
が可能)を使用することです。
mkfifo find_pipe
find … >find_pipe &
find_pid=$!
… <find_pipe
wait $find_pid
find_status=$?
どちらのアプローチも適切でない場合は、Perl、Python、Rubyなどのより高性能な言語に向かう必要があると思います。
coprocess を使用します。 coproc
ビルトインを使用して、サブプロセスを開始し、その出力を読み取り、その終了ステータスを確認できます。
coproc LS { ls existingdir; }
LS_PID_=$LS_PID
while IFS= read i; do echo "$i"; done <&"$LS"
wait "$LS_PID_"; echo $?
ディレクトリが存在しない場合、wait
はゼロ以外のステータスコードで終了します。
wait
が呼び出される前に$LS_PID
が設定解除されるため、現在、PIDを別の変数にコピーする必要があります。詳細については、 Bashはcoprocを待機する前に* _PID変数を設定解除します を参照してください。
1つのアプローチは次のとおりです。
status=0
token="WzNZY3CjqF3qkasn" # some random string
while read line; do
if [[ "$line" =~ $token:([[:digit:]]+) ]]; then
status="${BASH_REMATCH[1]}"
else
echo "$line"
fi
done < <(command; echo "$token:$?")
echo "Return code: $status"
コマンドの完了後に、ランダムトークンとともに終了ステータスをエコーし、bash正規表現を使用して、終了ステータスを検索して抽出するという考え方です。トークンは、出力で検索する一意の文字列を作成するために使用されます。
これは、一般的なプログラミングの意味でそれを行うための最良の方法ではないかもしれませんが、bashで処理するのに最も苦痛の少ない方法かもしれません。