サブシェル変数制限を呼び出さずに「&&」または「||」後にコマンドをグループ化する方法は? [コピー]

サブシェル変数制限を呼び出さずに「&&」または「||」後にコマンドをグループ化する方法は? [コピー]

スクリプトは単にファイル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

質問:

コピーが失敗または成功した場合、またはブランチ||は含まれているブランチが示すように、変数を&&正しくキャプチャしてそれぞれに設定しますerrorlevel10echo $errorlevel

ただし、次のコマンド(また、echo)は初期変数値を返します8。なぜこれが起こるのか理解できません。

いくつかの注意:

  1. 私はこのスクリプトをできるだけ安全にする必要があるため、明示的に&&andを使用します(実際には1500行のスクリプトの小さなルーチンです)。ゼロ以外の戻りキャプチャを使用しない場合、スクリプトは終了します。または、andを使用せずにコピールーチンを実行してから(エラーレベルを一度キャプチャして)再利用できますが、スクリプトの残りのスタイルと一致しないため、そうしないことをお勧めします。 。||set -e||set +e&&||set -e
  2. 私は常に私のシャットダウンを上書きしないようにset -o pipefailパイプラインでゼロ以外のエラーレベルを渡すために使用します。tee -a "$logfile"errorlevel
  3. 前者はゼロエラーレベルを返し、後者はゼロ以外のエラーレベルを返すので、代わりに++count使用します(そしてコメント#1の説明のために醜いことをする必要があります)。count++(( count++ )) || trueset -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操作をトリガーするのを防ぎます。

(もちろん、元の問題は( )サブシェルを作成して、変数の割り当てが親シェルに影響を与えないようにすることでした。)

関連情報