bash : 프로세스 대체에서 오류를 전파하는 방법?


19

명령으로 실행될 때마다 쉘 스크립트가 실패하기를 원합니다.

일반적으로 나는 다음과 같이합니다.

set -e
set -o pipefail

(일반적으로 나는 set -u또한 추가 )

문제는 위의 어느 것도 프로세스 대체와 함께 작동하지 않는다는 것입니다. 이 코드는 "ok"를 출력하고 리턴 코드 = 0으로 종료하지만 실패하고 싶습니다.

#!/bin/bash -e
set -o pipefail
cat <(false) <(echo ok)

"pipefail"과 동일하지만 프로세스 대체를위한 것이 있습니까? 파일처럼 명령의 출력을 명령에 전달하는 다른 방법이지만 해당 프로그램이 실패 할 때마다 오류가 발생합니까?

가난한 사람의 솔루션은 해당 명령이 stderr에 기록되는지 감지하지만 (일부 명령은 성공적인 시나리오에서 stderr에 기록)

posix를 준수하는 또 다른 솔루션은 명명 된 파이프를 사용하는 것이지만 컴파일 된 코드에서 즉시 빌드 된 oneliners로 사용하는 프로세스 대체 명령을 시작해야하며 명명 된 파이프를 만들면 문제가 복잡해집니다 (추가 명령, 트래핑 오류) 삭제 등)


명명 된 파이프를 삭제하기 위해 오류를 잡을 필요가 없습니다. 사실, 그것은 전혀 좋은 방법이 아닙니다. 고려하십시오 mkfifo pipe; { rm pipe; cat <file; } >pipe. 이 명령은 pipe셸이기 때문에 리더가 열릴 때까지 중단 open()됩니다 pipe. fs 링크에 대한 리더가있는 즉시 d pipe가 링크 된 rm다음 cat해당 파이프의 셸 설명자에 파일 을 복사합니다. 어쨌든 프로세스 하위에서 오류를 전파하려면 do: <( ! : || kill -2 "$$")
mikeserv

명명 된 파이프를 삭제하는 데 도움을 주셔서 감사합니다. 불행히도, $$프로세스 대체를 사용하는 명령이 "비쉘"코드 (파이썬)에서 생성 된 명령 파이프 라인 내에서 수행되므로 명령 대체가 수행되지 않으므로 대체가 작동하지 않습니다. 아마도 파이썬에서 하위 프로세스를 만들고 프로그래밍 방식으로 파이프해야합니다.
juanleon

따라서 사용하십시오 kill -2 0.
mikeserv

불행히도 "kill -2 0"은 (신호) 파이썬을 죽일 것입니다. 멀티 스레드 애플리케이션 내에서 비즈니스 로직을 수행하기위한 신호 처리기를 작성하는 것은 내가 기대하는 것이 아니다.
juanleon

신호를 처리하지 않으려면 왜 신호를 수신하려고합니까? 어쨌든, 명명 된 파이프는 결국 가장 간단한 솔루션이 될 것으로 기대합니다. 올바르게 수행하려면 자체 프레임 워크를 배치하고 맞춤형 디스패처를 설정해야하기 때문입니다. 그 장애물을 극복하고 모두 함께 모입니다.
mikeserv

답변:


6

예를 들어 다음과 같은 문제로 해결할 수 있습니다.

cat <(false || kill $$) <(echo ok)
other_command

스크립트의 서브 쉘은 SIGTERM두 번째 명령이 실행되기 전에 d입니다 ( other_command). 이 echo ok명령은 "때때로"실행됩니다. 문제는 프로세스 대체가 비동기 적이라는 것입니다. 한다는 보장이 없습니다 kill $$명령을 실행 하기 전에 또는 후에echo ok 명령. 운영 체제 예약 문제입니다.

다음과 같은 bash 스크립트를 고려하십시오.

#!/bin/bash
set -e
set -o pipefail
cat <(echo pre) <(false || kill $$) <(echo post)
echo "you will never see this"

해당 스크립트의 출력은 다음과 같습니다.

$ ./script
Terminated
$ echo $?
143           # it's 128 + 15 (signal number of SIGTERM)

또는:

$ ./script
Terminated
$ pre
post

$ echo $?
143

시도해 볼 수 있으며 몇 번 시도하면 출력에 두 가지 다른 순서가 표시됩니다. 첫 번째 스크립트에서는 다른 두 echo명령이 파일 설명자에 쓰기 전에 스크립트가 종료되었습니다 . 두 번째 명령 에서 false또는 kill명령은 명령 이후에 예약되었습니다 echo.

더 정확하게 될 : 시스템 호출 signal()kill상기 전송 utillity SIGTERM쉘 프로세스 신호 또는 이상 에코 이전 예약 된 (또는 전송 된) write()콜.

그러나 스크립트가 중지되고 종료 코드가 0이 아니므로 문제를 해결해야합니다.

물론 다른 해결책 은 명명 된 파이프를 사용하는 것입니다. 그러나 명명 된 파이프 또는 위의 해결 방법을 구현하는 것이 얼마나 복잡한지는 스크립트에 따라 다릅니다.

참고 문헌 :


2

기록을 위해, 답변과 의견이 좋았고 도움이 되더라도 약간 다른 것을 구현하지 않았습니다 (상위 프로세스에서 언급하지 않은 부모 프로세스에서 신호 수신에 대한 제한이있었습니다)

기본적으로 나는 다음과 같은 일을 끝내었다.

command <(subcomand 2>error_file && rm error_file) <(....) ...

그런 다음 오류 파일을 확인합니다. 존재하는 경우 어떤 하위 명령이 실패했는지 알고 있으며 error_file의 내용이 유용 할 수 있습니다. 원래 원했던 것보다 더 장황하고 해킹이 있지만 한 줄짜리 bash 명령으로 명명 된 파이프를 만드는 것보다 번거롭지 않습니다.


당신을 위해 일하는 것은 일반적으로 가장 좋은 방법입니다. 돌아와 답장을 보내 주셔서 감사합니다. 셀카는 내가 가장 좋아하는 것입니다.
mikeserv

2

이 예는와 kill함께 사용하는 방법을 보여줍니다 trap.

#! /bin/bash
failure ()
{
  echo 'sub process failed' >&2
  exit 1
}
trap failure SIGUSR1
cat < <( false || kill -SIGUSR1 $$ )

그러나 kill하위 프로세스에서 상위 프로세스로 리턴 코드를 전달할 수 없습니다.


허용 대답에이 변화와 같은 I
sehe

1

POSIX 쉘을 지원하지 않는 / 구현$PIPESTATUS$pipestatus 하는 것과 비슷한 방식으로 파이프를 통해 명령을 전달하여 명령의 종료 상태를 얻을 수 있습니다.

unset -v false_status echo_status
{ code=$(
    exec 3>&1 >&4 4>&-
    cat 3>&- <(false 3>&-; echo >&3 "false_status=$?") \
             <(echo ok 3>&-; echo >&3 "echo_status=$?")
);} 4>&1
cat_status=$?
eval "$code"
printf '%s_code=%d\n' cat   "$cat_status" \
                      false "$false_status" \
                      echo  "$echo_status"

다음을 제공합니다.

ok
cat_code=0
false_code=1
echo_code=0

또는 pipefail지원하지 않는 쉘에서와 같이 수동으로 프로세스 대체를 사용 하고 구현할 수 있습니다 .

set -o pipefail
{
  false <&5 5<&- | {
    echo OK <&5 5<&- | {
      cat /dev/fd/3 /dev/fd/4
    } 4<&0 <&5 5<&-
  } 3<&0
} 5<&0
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.