내 함수에 파이프 오류가 발생하여 1회 실패 후 명령이 무시됩니다.

내 함수에 파이프 오류가 발생하여 1회 실패 후 명령이 무시됩니다.

컨텍스트: 파일을 복사하는 Bash 스크립트가 있습니다.

function log () {
    read IN
    if [ "$IN" == "" ]; then
        :
    else

        echo "$datetime"$'\t'"$IN" | tee -a logfile
    fi
}

function copy () {
    command cp -L --parents $@
}

...
copy -R /etc . 2>&1 | log
...

문제: cp -L --parents -R /etc 2>&1수동으로 실행할 때 약 10번의 실패(예상되는 깨진 심볼릭 링크)가 발생했고 전체 /etc가 복사되었습니다.

그러나 스크립트가 실행되면 1개의 실패만 보고되고 /etc는 1개의 실패가 발생한 위치에만 복사됩니다.

문제를 해결하려고 시도하는 동안 내가 한 일은 2>&1스크립트에서 해당 문제를 제거하는 것뿐이었고 복제는 예상대로 작동했습니다.

질문: 문제를 일으키는 것이 내 기능입니까 log, 아니면 스크립트가 작성되는 방식에 구문 문제가 있습니까(스크립트가 손상되지는 않지만)?

답변1

귀하의 log기능이 범인입니다. 그것이 하는 일은: 라인을 읽고, 라인이 비어 있지 않으면 타임스탬프를 인쇄한 다음, 라인의 내용을 인쇄하는 것입니다. 그것이 하는 전부입니다. 일단 행이 처리되면 반환됩니다.

cp첫 번째 오류 메시지가 나타나면 함수 는 log이를 읽고 처리합니다. 그런 다음 함수 log가 반환되므로 파이프 오른쪽의 프로세스가 종료되고 이로 인해 파이프의 읽기 끝이 닫힙니다. 두 번째 오류 메시지가 발행 되면 cp닫힌 파이프에 쓰기를 시도하며 이로 인해SIGPIPE 신호. 표준 오류는 라인 버퍼링되므로(기본적으로 cp이를 변경하려고 시도하지 않음) 버퍼링은 효과가 없습니다.

모든 입력 행을 처리하려면 반복해야 합니다 read.

log () {
    while IFS= read -r IN; do
        echo "$datetime"$'\t'"$IN"
    done | tee -a logfile >&2
}

나도 read통화를 고쳤어IFS= read -r실제로 한 줄을 읽으십시오. 빈 줄에 대한 특별한 처리를 제거했는데 그것은 무의미했습니다(입력에 빈 줄이 없습니다). 입력이 비어 있는 경우(입력 0줄)를 처리하기 위해 넣은 것 같지만 이를 처리하는 올바른 방법은 명령의 반환 상태를 확인하는 것입니다 read. 또한 log오류 메시지를 처리하는 데 사용되는 표준 오류 인쇄를 수정했습니다 .

바라보다명령 출력의 각 줄 앞에 타임스탬프를 추가합니다.이를 수행하는 다른 방법에 대해 알아보세요.

파이프의 왼쪽에 명령을 배치하면 한 가지 큰 단점이 있습니다.종료 상태가 무시됩니다.. 따라서 cp실패하면 스크립트가 계속 진행됩니다. 오류는 어딘가에 기록되지만 로그를 읽어야 한다는 알림 없이 후속 명령이 정상적으로 실행됩니다. Bash, ksh 또는 zsh에서 설정할 수 있습니다pipefail옵션또한 set -e명령이 실패하면 파이프라인 왼쪽에서도 스크립트가 오류 상태로 종료됩니다.

set -o errexit -o pipefail
copy … |& log

또는 다음을 사용하십시오.프로세스 교체다른 프로세스를 통해 오류 출력을 파이핑하는 대신. 프로세스 대체에 대한 고려 사항은 파이프의 경우와 약간 다릅니다. 오류는 log사실상 무시되며 명령은 log완료되기 전에 반환될 수 있습니다(bash에서는 log백그라운드에서 실행 중).

set -e
copy … 2> >(log)

거의 더 이상 읽을 필요가 없으며 내용을 망치는 것도 불가능합니다.IFS= read -r IN

관련 정보