for 루프의 조건 continue는 함수의 오류 종료를 방해합니다.

for 루프의 조건 continue는 함수의 오류 종료를 방해합니다.

AFAICT, continuefor 루프 내에서 다른 함수를 호출하면 errexit의미가 손상됩니다. main()함수에서 오류가 발생하면 다음 반복을 계속하고 싶습니다 build().

#! /usr/bin/env bash

export PS4='# ${BASH_SOURCE}:${LINENO}: ${FUNCNAME[0]}() - [${SHLVL},${BASH_SUBSHELL},$?] '
set -o xtrace
set -o errexit

build() {
  local _foo=$1

  if [ "${_foo}" -eq 1 ]; then
    false
  fi

  printf "%s with foo=%s builds ok\\n" "${FUNCNAME[0]}" "${_foo}"
}

main() {
  for i in 1 2 3; do
    build $i || continue
  done
}

main "$@"

그러나 루프 continue내부에서는 for코드가 발생합니다.계속하다대신, 함수 내부에서 build()플래그의 효과를 제거하십시오 errexit.

$ ./foo.sh 
# ./foo.sh:5: () - [3,0,0] set -o errexit
# ./foo.sh:23: () - [3,0,0] main
# ./foo.sh:18: main() - [3,0,0] for i in 1 2 3
# ./foo.sh:19: main() - [3,0,0] build 1
# ./foo.sh:8: build() - [3,0,0] local _foo=1
# ./foo.sh:10: build() - [3,0,0] '[' 1 -eq 1 ']'
# ./foo.sh:11: build() - [3,0,0] false
# ./foo.sh:14: build() - [3,0,1] printf '%s with foo=%s builds ok\n' build 1
build with foo=1 builds ok
# ./foo.sh:18: main() - [3,0,0] for i in 1 2 3
# ./foo.sh:19: main() - [3,0,0] build 2
# ./foo.sh:8: build() - [3,0,0] local _foo=2
# ./foo.sh:10: build() - [3,0,0] '[' 2 -eq 1 ']'
# ./foo.sh:14: build() - [3,0,0] printf '%s with foo=%s builds ok\n' build 2
build with foo=2 builds ok
# ./foo.sh:18: main() - [3,0,0] for i in 1 2 3
# ./foo.sh:19: main() - [3,0,0] build 3
# ./foo.sh:8: build() - [3,0,0] local _foo=3
# ./foo.sh:10: build() - [3,0,0] '[' 3 -eq 1 ']'
# ./foo.sh:14: build() - [3,0,0] printf '%s with foo=%s builds ok\n' build 3
build with foo=3 builds ok

printf이전 줄의 종료 코드가 있는 줄에서 볼 수 있듯이 false이는 실제로 1(앞 대괄호 안의 세 번째 숫자) 이므로 errexit제자리에 없는 것처럼 실행됩니다.

# ./foo.sh:14: build() - [3,0,1] printf '%s with foo=%s builds ok\n' build 1

|| continue삭제하면 쉘이 종료되어 i=1subhshell errexit/ 함수에 전달되는 것을 확인했습니다 .

어떤 도움이라도 대단히 감사하겠습니다.

버전

~ $ bash --version                                                            
GNU bash, version 5.0.3(1)-release (x86_64-pc-linux-gnu)

고쳐 쓰다

다음 질문에 대한 좋은 답변이 많이 있습니다이것은. 문제를 해결하는 방법에 관해서는 이 솔루션이 내가 원하는 작업을 수행하도록 스크립트를 얻는 가장 쉬운 방법이라는 것을 알았습니다. 스크립트를 다음으로 변경하세요 false.

false || return $?

물론 단점은 함수가 호출하는 모든 명령에 대해 이 작업을 수행해야 한다는 것입니다. run()전달된 명령을 실행하고 반환 코드를 확인한 후 그에 따라 스크립트를 실패시키는 래퍼를 사용하는 이전 방식 으로 돌아가야 할 수도 있습니다 . 나는 errexit당신이 할 것으로 예상되는 일을 할 것 같아요 :-)

답변1

-e이는 / -errexit의 설명과 일치하는 것 같습니다.배쉬 문서:

실패한 명령이 while 또는 Until 키워드(if 문의 테스트 부분) 바로 뒤에 있는 명령 목록의 일부인 경우 쉘은 종료되지 않습니다.&& 또는 || 내에서 실행되는 명령의 일부 목록마지막 && 또는 || 뒤의 마지막 명령을 제외한 파이프라인의 모든 명령이나 명령의 반환 상태는 !로 반전됩니다.
[...]

-e를 생략한 컨텍스트에서 복합 명령 또는 셸 함수가 실행되는 경우 -e가 설정되고 명령이 오류를 반환하더라도 복합 명령 또는 함수의 본문 내에서 실행되는 모든 명령은 -e 설정의 영향을 받지 않습니다. 상태.

이것은 이미이 계산기 문제,다음으로 연결이 이메일그리고 다음 텍스트가 함께 제공됩니다.

> My initial gripe about errexit (and its man page description) is that the 
> following doesn't behave as a newbie would expect it to:
> 
> set -e
> f() {
>   false
>   echo "NO!!"
> }
> f || { echo "f failed" >&2; exit 1; }

Indeed, the correct behavior mandated by POSIX (namely, that 'set -e' is
completely ignored for the duration of the entire body of f(), because f
was invoked in a context that ignores 'set -e') is not intuitive.  But
it is standardized, so we have to live with it.

이것POSIX설명은 -e다음과 같습니다.

-e
이 옵션이 켜져 있으면 쉘 오류의 결과에 나열된 이유로 인해 간단한 명령이 실패하거나 종료 상태 값 >0을 반환하고 while, Until 또는 if 키워드 뒤에 오는 복합 목록의 일부가 아닌 경우 , 그리고AND 또는 OR 목록의 일부가 아님, 앞에는 파이프가 없습니다. 예약어를 사용하면 쉘이 즉시 종료되어야 합니다.

답변2

~에서수동[강조 내]:

errexit

와 동일합니다 -e.

-e

파이프라인 [...](단일 단순 명령 [...], 목록 [...] 또는 복합 명령 [...]으로 구성될 수 있음)이 0이 아닌 상태를 반환하는 경우 즉시 종료하십시오.실패한 명령이 &&[...]의 일부이거나 목록에서 실행된 명령( ||마지막 또는 다음 명령 제외)인 경우 쉘은 종료되지 않습니다.&&||,[…]

[…]

[...] 쉘 함수가 무시된 컨텍스트에서 실행되는 경우 [ -e...] 함수 본문 내에서 실행되는 모든 명령은 이 설정이 설정되고 명령이 실패 상태를 반환 -e하더라도 이 설정의 영향을 받지 않습니다. -e.[…]

쉘 기능은 무시된 build $i || continue build컨텍스트에서 실행됩니다. 함수 본문 내에서 실행되는 명령이므로 설정의 영향을 받지 않으므로 실행을 방해하지도 않습니다.-efalse-eprintf

제거 || continue하고 호출하면 build $i함수의 각 부분이 다음 -e과 같은 컨텍스트에 놓이게 됩니다.아니요false은 무시되므로 및 이후 로 인해 전체 코드가 false도착하지 않고 종료됩니다 printf.

이는 errexit(무시하지 않을 경우) 전체 스크립트를 종료하는 전역 설정인 것 같습니다. 함수를 종료하도록 할 수는 없지만(또는 적어도 쉽게는 할 수 없음) 전체 스크립트를 종료할 수는 없습니다.

관련 정보