이전 명령(tar)도 실패한 경우 awk가 실패하도록 하려면 어떻게 해야 합니까?

이전 명령(tar)도 실패한 경우 awk가 실패하도록 하려면 어떻게 해야 합니까?

tar 파일을 추출하고 초당 추출된 파일 수를 인쇄하는 다음 명령에 몇 가지 문제가 있습니다.

tar -xvf some_tar.tar -C a/directory | awk 'systime() > lasttime { lasttime = systime(); printf "%d files\n", NR; fflush(stdout) }'

tar 명령이 실패하더라도 awk 명령은 여전히 ​​0을 반환하는데, 이는 tar 명령의 실패를 반영하지 않기 때문에 바람직하지 않습니다.

이 문제를 어떻게 해결할 수 있나요?

답변1

pipefail파이프라인의 명령이 실패했는지 확인하려면 이 옵션을 설정하십시오 . Bash 외에도 ksh, zsh 및 Busybox(적어도)도 이를 지원합니다. 이 옵션이 설정되면 파이프의 종료 상태는 관련 명령에서 반환된 가장 왼쪽의 0이 아닌 종료 상태입니다.

$ set -o pipefail
$ (exit 123) | true
$ echo $?
123

또는 파이프가 조건부입니다("실패했습니다"라고 표시되어야 함).

set -o pipefail
if false | true; then
    echo it succeeded
else
    echo it failed
fi

답변2

최신 버전의 한 가지 접근 방식은 bash명령 파이프라인을 호출한 후 배열 변수의 값을 확인하는 것입니다. Bash 매뉴얼 페이지에 따르면:PIPESTATUStar|awk

PIPESTATUS
   An array variable (see Arrays below) containing a list of exit status
   values from the processes  in  the  most-recently-executed foreground
   pipeline (which may contain only a single command).

따라서 tar의 종료 코드는 에 있고 ${PIPESTATUS[0]}awk의 종료 코드는 에 있습니다 ${PIPESTATUS[1]}.

답변3

파이프는 전혀 사용되지 않습니다. 명명된 파이프를 사용합니다.

mkfifo p
awk '...' < p &
tar -xvf some_tar.tar -C a/directory > p
echo $?

awk명령은 백그라운드에서 실행되며 tar명명된 파이프에 대한 쓰기가 시작될 때까지 차단됩니다. tar파이프 끝을 종료하고 닫으면 끝 awk에서 나머지 내용을 읽은 후 종료됩니다. 이 명령은 대신 종료 상태를 echo보고합니다 .tarawk

답변4

각 입력 라인을 호출하면 파이프라인 속도가 크게 느려질 것으로 예상되므로 systime()파이프라인의 출력은 초당 tar 추출되는 파일 수를 정확하게 반영하지 않습니다. 진행률 표시기를 보려면 systime()천 또는 백만 또는 일부 입력 라인마다 한 번만 호출하는 것을 고려하거나 전혀 호출하지 않고 천 또는 백만 입력 라인마다 인쇄하는 것을 고려하십시오. 초당 파일을 가져오는 중입니다 systime().

요청한 문제를 해결하고 호출의 오버헤드를 거의 완전히 제거하려면 다음과 같은 작업을 수행하는 것을 고려하십시오 systime()(GNU awk를 시간 함수로 사용하고 읽은 $0마지막 END줄을 포함하고 입력에 NUL을 포함할 수 있음).

{ tar -xvf some_tar.tar -C a/directory && printf '\0\n'; } |
awk -v n=1000000 '
    BEGIN { beg = systime() }
    NR%n == 0 { printf "%d files processed\n", NR }
    END {
        end = systime()
        if ( $0 == "\0" ) {
            numFiles = NR - 1
            exitStatus = 0
        }
        else {
            numFiles = NR
            exitStatus = 1
        }
        printf "%d files per sec\n", numFiles / (end > beg ? end - beg : 1)
        exit exitStatus
    }
'

관련 정보