![파이프의 첫 번째 명령 실패 시 set -o Pipefail을 적용하는 방법](https://rvso.com/image/83667/%ED%8C%8C%EC%9D%B4%ED%94%84%EC%9D%98%20%EC%B2%AB%20%EB%B2%88%EC%A7%B8%20%EB%AA%85%EB%A0%B9%20%EC%8B%A4%ED%8C%A8%20%EC%8B%9C%20set%20-o%20Pipefail%EC%9D%84%20%EC%A0%81%EC%9A%A9%ED%95%98%EB%8A%94%20%EB%B0%A9%EB%B2%95.png)
Postgres 데이터베이스의 데이터를 Bash의 파일로 내보내려고 합니다. 하지만 데이터베이스 연결이 실패하지 않는 경우에만 파일을 덮어쓰고 싶습니다(예: 일부 데이터를 다시 가져옴).
사용을 시도했습니다파이프실패그러나 첫 번째 명령이 오류로 인해 실패하는 경우(예를 들어 호스트가 존재하지 않는 경우) cat 명령은 계속 실행되어 빈 파일을 생성합니다(방지하고 싶은 마지막 좋은 콘텐츠를 삭제합니다). 아래 예에서 myhost는 잘못된 호스트이므로 psql 명령이 실패합니다.
따라서 더 큰 질문은 파이프 실패가 설정된 경우 첫 번째 명령이 실패할 때 후속 명령이 실행되지 않도록 하는 방법입니다.
#!/bin/sh
set -o nounset
set -o errexit
set -o pipefail
PG_HOST=myhost
psql $PG_HOST -At -F$'\t' -c "SELECT * FROM mytable" | cat > /tmp/mytable.txt
답변1
set -o pipefail -o errexit
후속 명령이 실행되는 것을 방지하지만 이는 도움이 되지 않습니다.후속명령이 실행되지 않습니다. 파이프라인에서는 producer | consumer
및 명령이 실행됩니다 producer
.consumer
병행하여. 이상한 타이밍 사고를 제외하고는 이미 시작되었기 때문에 실패 consumer
하면 시작을 막을 수 없습니다 .producer
유일한 두 가지 가능성이 " consumer
성공하고 비어 있지 않은 출력을 생성합니다"와 "consumer
실패하고 출력을 생성하지 않음"인 경우 다음을 사용할 수 있습니다.ifne
Joey Hess의 moreutils에서.
producer | ifne consumer
하지만 귀하의 사용 사례에서는 작동하지 않을 것 같습니다. 일치하는 행이 없을 수도 있고(거짓음성, 오래된 데이터를 얻음), 데이터베이스 연결이 중간에 끊어질 수도 있습니다(거짓양성, 잘린 데이터를 얻을 수 있습니다). ).
생산자가 성공했는지 여부를 알아야 하는 경우 소비자를 시작하기 전에 완료될 때까지 기다려야 합니다. 그리고 소비자가 아직 주변에 없기 때문에 출력을 저장해야 하는 것이 필요합니다.
출력에 널 바이트가 포함되지 않고 단 하나의 개행 문자로 끝나며 지나치게 크지 않은 경우 이를 쉘 변수에 저장할 수 있습니다.
output=$(producer); producer_status=$?
if [ "$producer_status" -ne 0 ]; then
echo >&2 "Producer failed with status $producer_status"
exit "$producer_status"
fi
printf '%s\n' "$output" | consumer
zsh와 ksh93 및 bash를 포함한 몇 가지 다른 셸에서는 마지막 줄을 consumer <<<"$output"
.
명령 대체는 후행 줄 바꿈을 제거합니다. 후행 빈 줄이 관련된 경우 해결 방법은 첫 번째 줄을 다음으로 변경하는 것입니다.
output=$(producer; ret=$?; echo .; exit "$?")
producer_status=$? output=${output%?}
$output
그런 다음 후행 개행 문자를 포함한 전체 출력이 포함됩니다. 그런 다음 printf %s "$output"
대신 printf '%s\n' "$output"
을 사용하여 consumer
.
출력이 잠재적으로 너무 크거나 널 바이트를 포함할 수 있는 경우 임시 파일에 저장하십시오.
답변2
DopeGhoti가 말했듯이,pipefail
... 단지 파이프 체인의 어느 지점에서든 오류가 종료 코드에 대해 보존된다는 의미일 뿐입니다.[파이프라인의].
오류 발생 시 스크립트가 종료되도록 하려면 를 사용하십시오 set -e
.
파일 생성을 방지하려면 임시 파일을 생성하고 성공 시 이름을 바꾸십시오. 즉, 다음과 같습니다.
set -e
psql $PG_HOST -At -F$'\t' -c \
"SELECT * FROM mytable" > /tmp/mytable.txt~
# ^^^ cf. Useless Use of Cat
mv /tmp/mytable.txt~ /tmp/mytable.txt
나는 항상 사용만들다이런 종류의 경우 오류 발생 시 중지되고 다시 시작 가능한 파이프라인을 구축할 수 있기 때문입니다.