서브셸 변수 제한을 호출하지 않고 "&&" 또는 "||" 뒤에 명령을 그룹화하는 방법은 무엇입니까? [복사]

서브셸 변수 제한을 호출하지 않고 "&&" 또는 "||" 뒤에 명령을 그룹화하는 방법은 무엇입니까? [복사]

스크립트는 단순히 파일을 foo에 복사하려고 시도하고 bar/, 실패할 경우 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.10echo $errorlevel

그러나 다음 명령(또한 echo)은 초기 변수 값을 반환합니다 8. 왜 이런 일이 발생하는지 이해할 수 없습니다.

몇 가지 참고사항:

  1. 나는 이 스크립트를 가능한 한 안전하게 만들어야 하기 때문에 명시적으로 &&and를 사용합니다 (실제로는 1500줄 스크립트의 작은 루틴입니다). 0이 아닌 반환 캡처를 사용하지 않으면 스크립트가 종료됩니다. 또는 and를 사용하지 않고도 복사 루틴을 수행한 다음 (오류 수준을 한 번 포착하여) 다시 사용할 수 있지만 스크립트의 나머지 스타일과 일치하지 않기 때문에 그렇게 하지 않는 것이 좋습니다. .||set -e||set +e&&||set -e
  2. 나는 항상 내 종료를 덮어쓰지 않도록 set -o pipefail파이프라인에서 0이 아닌 오류 수준을 전달하는 데 사용합니다 .tee -a "$logfile"errorlevel
  3. 전자는 0 오류 수준을 반환하고 후자는 0이 아닌 오류 수준을 반환하기 때문에 ++count대신 사용합니다 (그리고 주석 #1의 설명 으로 인해 추악한 작업을 수행해야 합니다).count++(( count++ )) || trueset -e

errorlevel이제 변수를 명시적으로 설정하여 이 1문제를 해결했습니다. 0이 아닌 값이 무엇인지 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

if0이 아닌 종료 상태가 -e잘못된 종료 작업을 트리거하는 것을 방지합니다 .

(물론 원래 문제는 ( )하위 쉘을 생성하여 변수 할당이 상위 쉘에 영향을 미치지 않도록 하는 것이었습니다.)

관련 정보