![サブシェル変数制限を呼び出さずに「&&」または「||」後にコマンドをグループ化する方法は? [コピー]](https://linux33.com/image/119480/%E3%82%B5%E3%83%96%E3%82%B7%E3%82%A7%E3%83%AB%E5%A4%89%E6%95%B0%E5%88%B6%E9%99%90%E3%82%92%E5%91%BC%E3%81%B3%E5%87%BA%E3%81%95%E3%81%9A%E3%81%AB%E3%80%8C%26amp%3B%26amp%3B%E3%80%8D%E3%81%BE%E3%81%9F%E3%81%AF%E3%80%8C%7C%7C%E3%80%8D%E5%BE%8C%E3%81%AB%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%82%92%E3%82%B0%E3%83%AB%E3%83%BC%E3%83%97%E5%8C%96%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95%E3%81%AF%EF%BC%9F%20%5B%E3%82%B3%E3%83%94%E3%83%BC%5D.png)
スクリプトは単にファイルfoo
をにコピーしようとしbar/
、失敗した場合はもう2回再試行します。
編集する:2番目の例はうまくいき、最初の例はうまくいかないのは、(...)
or分岐後にコマンドセットを実行することがサブシェルと見なされ、外部変数に影響を与えないからです。&&
||
したがって、修正された質問は次のようになります。最初の例のように、いくつかのコマンドをグループ化しますが、サブシェルを使用せずに実行するにはどうすればよいですか?
編集2:答え( ... )
は{ ...; }
!
#!/usr/bin/env bash
#
# Be safe
set -euo pipefail
# Init variables
__file="foo"
output_dir="bar"
logfile="test.log"
errorlevel=8
count=0
# Try three times to copy a file
until (( errorlevel == 0 || count == 3 )); do
cp -Lv --no-preserve=mode \
"$__file" \
"$output_dir/" \
| tee -a "$logfile" \
&& (errorlevel=$?; echo "zero return, $errorlevel") \
|| (errorlevel=$?; echo "non-zero return, $errorlevel")
echo "errorlevel $errorlevel"
(( ++count ))
echo "count $count"
done
unset errorlevel
unset count
質問:
コピーが失敗または成功した場合、またはブランチ||
は含まれているブランチが示すように、変数を&&
正しくキャプチャしてそれぞれに設定しますerrorlevel
。1
0
echo $errorlevel
ただし、次のコマンド(また、echo)は初期変数値を返します8
。なぜこれが起こるのか理解できません。
いくつかの注意:
- 私はこのスクリプトをできるだけ安全にする必要があるため、明示的に
&&
andを使用します(実際には1500行のスクリプトの小さなルーチンです)。ゼロ以外の戻りキャプチャを使用しない場合、スクリプトは終了します。または、andを使用せずにコピールーチンを実行してから(エラーレベルを一度キャプチャして)再利用できますが、スクリプトの残りのスタイルと一致しないため、そうしないことをお勧めします。 。||
set -e
||
set +e
&&
||
set -e
- 私は常に私のシャットダウンを上書きしないように
set -o pipefail
パイプラインでゼロ以外のエラーレベルを渡すために使用します。tee -a "$logfile"
errorlevel
- 前者はゼロエラーレベルを返し、後者はゼロ以外のエラーレベルを返すので、代わりに
++count
使用します(そしてコメント#1の説明のために醜いことをする必要があります)。count++
(( count++ )) || true
set -e
errorlevel
これで、変数を明示的に設定してこの1
問題を解決しました。ゼロ以外の値が何であるか0
は実際には気にしないからですerrorlevel
。しかし、まだ上記の内容が期待どおりに機能しない理由を知りたいです。
解決策は次のとおりです。
#!/usr/bin/env bash
#
set -euo pipefail
__file="foo"
output_dir="bar"
logfile="test.log"
errorlevel=1
count=0
until (( errorlevel == 0 || count == 5 )); do
cp -Lv --no-preserve=mode \
"$__file" \
"$output_dir/" \
| tee -a "$logfile" \
&& errorlevel=0 \
|| errorlevel=1
(( ++count ))
sleep 1
done
unset errorlevel
unset count
どんな洞察力でも大変感謝します!
答え1
私はif
ブロックを使用することを好みます:
errorlevel=0
if cp -Lv --no-preserve=mode \
"$__file" \
"$output_dir/" \
| tee -a "$logfile"
then
echo "zero return, $errorlevel"
else
errorlevel=$?
echo "non-zero return, $errorlevel"
fi
if
ゼロ以外の終了状態が誤った終了-e
操作をトリガーするのを防ぎます。
(もちろん、元の問題は( )
サブシェルを作成して、変数の割り当てが親シェルに影響を与えないようにすることでした。)