컨텍스트: 파일을 복사하는 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