나는 이것이 불가능하다고 믿는다

나는 이것이 불가능하다고 믿는다

다음을 고려하세요:

command1 | command2 | command3

내가 이해한 바에 따르면 파이프라인은 발생할 수 있는 오류에 관계없이 모든 명령을 실행합니다. 명령이 stderr을 반환하면 다음 명령으로 파이프되지 않지만 다음 명령은 계속 실행됩니다( |&사용 하지 않는 한). 발생할 수 있는 오류가 있으면 나머지 파이프라인을 종료하고 싶습니다. 나는 이것을 달성하는 것이 가능하다고 생각 set -o pipefail하지만 파이프의 어떤 것이든 실패하면 파이프 뒤에 올 수 있는 모든 것을 종료합니다. 즉:

(set -o pipefail; cmd1 | cmd2 && echo "I won't run if any of the previous commands fail")

따라서 종료하는 가장 간단한 방법은나머지명령이 실패하면 파이프라인은 어떻게 되나요? 또한 실패한 명령에 대해 올바른 stderr을 사용하여 종료해야 합니다. 저는 쉘 스크립트가 아닌 명령줄 컨텍스트에서 이 작업을 수행하므로 단순성을 찾고 있습니다. 아이디어?

답변1

나는 이것이 불가능하다고 믿는다

나는 당신이 요구하는 것이 파이프 구현 방식으로 인해 직접적으로 가능하지 않다고 생각합니다. 셸이 파이프라인에서 "나중에" 명령을 실행할 때 명령의 성공 또는 실패(반환 값)를 알 수 없습니다. 실제로는 모두 동시에 실행한 다음 결과를 수집합니다.

도움이 될 수 있는 몇 가지 해결 방법이 있습니다.

솔루션 1

한 번에 하나의 명령을 실행하고 결과를 캐시합니다. 이전 명령이 실패하면 이후 명령이 실행되지 않기 때문에 이것이 더 좋습니다.

매우 짧은 스크립트 예:

cache_file=`tempfile`
if command1 > $cache_file ; then
    command2 < $cache_file
fi
rm $cache_file

솔루션 2

모든 것을 실행하고 반환 결과를 확인하십시오. 어쨌든 이렇게 하면 모든 명령이 실행되지만 다시 돌아가서 이유를 찾을 수 있습니다.

여기서 각 명령의 STDERR은 확장자가 2>.PIPESTATUS각 명령에 대한 반환 코드를 찾으려면 확인하세요.

command1 2> command1_err.log | command2 2> command2_err.log
for result in ${PIPESTATUS[@]} ; do
    if [ $result -ne 0 ] ; then
        echo command failed
    fi
done

셸에서 파이프라인 실행에 대한 간략한 개요

파이프를 생성하기 위해 쉘은 다음 단계를 따릅니다.~처럼이것들:

  1. 다음 명령( |) 을 사용하여 각 파이프를 생성합니다.관로(). 각 파이프에는 읽기 핸들과 쓰기 핸들이 있습니다. 각 리디렉션( <, >)에 대해 해당 파일을 열고 이를 사용하여 파일에 대한 핸들을 얻습니다.열려 있는().
  2. 쉘 호출십자가()파이프라인의 각 명령은 새 프로세스를 시작합니다.
  3. 각 하위 프로세스는 (1.)에서 생성된 핸들과 STDIN, STDOUT 및 STDERR 핸들을 교환합니다.
  4. 명령이 외부 바이너리라고 가정하면 각 하위 프로세스는 다음을 호출합니다.구현하다()바이너리를 로드하고 실행합니다.
  5. 그런 다음 상위 프로세스는 하위 프로세스가 사용을 마칠 때까지 기다립니다.기다리다()또한 명령의 반환 값(성공 또는 실패)도 제공합니다.

답변2

파이프라인을 중지하는 깔끔한 방법은 중간에 셸을 종료하는 것입니다. 이제 이는 대화형 셸을 종료한다는 의미이므로(터미널 에뮬레이터에서 실행하는 경우) 작업을 계속하려면 다른 셸을 시작해야 합니다. 그러나 이렇게 하면 최소한 다음 파이프라인 명령이 중지됩니다.

command1 2> command1_error.log | awk -v status=$? -v pid=$$ '{if (status != 0) { system("kill -9 " pid) } else { print } }' | command2 ..

참고: 터미널/tty에서 이 명령을 실행하면 로그아웃됩니다.

또는 마지막 명령을 차단하려는 경우(예: 파일을 생성하는 명령이 파이프라인의 마지막 명령임) 다음을 사용할 수 있습니다.xargs -r

command1 | command2 | xargs -r command3

이 플래그는 이전 명령이 아무 것도 출력하지 않은 경우 -rcommand3이 실행되는 것을 방지합니다 .xargs

관련 정보