- 출처 : 유닉스 리눅스 쉘스크립트 예제사전_한빛미디어
명령어: echo
키워드: 파이프, 종료 스테이터스
사용처: 파이프 처리 중에 어떤 명령어 종료 스테이터스를 확인해서 명령어의 성공/실패를 판단하고 싶을 때
실행예제
$ ./pipestatus.sh
[ERROR] sort-data.sh에 실패했습니다.
스크립트
#!/bin/bash
# 다음과 같은 처리를 하는 경우를 가정
# script.sh : 데이터 출력
# sort-data.sh : 데이터 정렬
# calc.sh : 출력 데이터 계산
./script.sh | ./sort-data.sh | ./calc.sh > output.txt #------- 1
# 다른 명령어를 실행하면 PIPESTATUS값이 사라지므로
# 결과를 복사해둠
pipe_status=("$PIPESTATUS[@]") #------------------------------ 2
# 파이프 처리 중에 명령어 성공, 실패 확인
# sort-data.sh 종료 스테이터스가 0이 아닌지 확인
if [ "${pipe_status[1]}" -ne 0 ]; then #---------------------- 3(if문)
echo "[ERROR] sort-data.sh에 실패했습니다." >&2
fi
해설
이 스크립트는 파이프 처리 중에 에러가 발생했는지 확인해서 에러가 발생하면 에러 메시지를 출력합니다.
여기서는 script.sh -> sort-data.sh -> calc.sh라는 세 스크립트를 순서대로 실행해서 어떤 데이터를 처리한 뒤에 그 결과를 output.txt라는 파일에 출력한다고 가정합니다.
여기서 sort-data.sh는 실패하기도 하는데 그 다음 calc.sh에서 하는 계산은 정렬이 필수가 아니므로 에러 발생 여부와 관계없이 실행 가능하다고 가정합니다. 다만 정렬에 실패하면 종료 스테이터스가 0이 아니므로 이걸 검출해서 에러 메시지만 출력하고 싶다고 합시다.
명령어 종료 스테이터스 $?를 이용하는 예제는 앞에서 많이 다루었지만 파이프 처리는 도중에 명령어 종료 스테이터스를 $?로 취득할 수 없습니다. 변수 $?는 파이프 라인 마지막에 실행된 명령어 종료 스테이터스만 대입되기 때문에 1
에서 마지막 calc.sh 종료 스테이터스만 확인할 수 있습니다.
그러나 bash 내장 변수인 PIPESTATUS를 이용하면 직접 파이프라인 처리의 모든 종료 스테이터스를 취득할 수 있습니다. PIPESTATUS는 배열 변수라서 0번에는 파이프 첫번째 종료 스테이터스가, 1번에는 파이프 두 번째 종료 스테이터스가 들어 있습니다.
-
예제에서 다루는 특수 변수 PIPESTATUS값
서술 예 설명 ${PIPESTATUS[0]} 파이프 처리 첫 번째(script.sh) 종료 스테이터스 ${PIPESTATUS[1]} 파이프 처리 두 번째(sort-data.sh) 종료 스테이터스 ${PIPESTATUS[2]} 파이프 처리 세 번째(calc.sh) 종료 스테이터스
2
의 ${PIPESTATUS[@]}로 사용하는 배열 첨자 @은 배열 전체를 의미합니다. 전체를 ()로 싸서 배열 전체를 셸 변수에 복사합니다.
3
은 sort-data.sh 종료 스테이터스를 ${PIPESTATUS[1]}로 취득해서 TEST 명령어의 다른 값인지 확인하는 -ne 연산자로 0과 비교합니다. 종료 스테이터스가 0이 아니면 에러 메시지를 표시합니다.
이렇게 해서 파이프 처리 중에 명령어 종료 스테이터스를 취득할 수 있습니다. 세세한 에러 처리를 하고 싶을 때 사용하면 되겠습니다.
## PIPESTATUS 이용 시 주의점
변수 PIPESTATES를 이용할 때 주의해야 하는 것은 어떤 명령어를 실행할 때마다 변수 PIPESTATUS가 늘 변경된다는 점입니다. 예를 들어 다음은 파이프 처리하는 세 명령어 종료 스테이터스를 echo 명령어로 확인하는(하려고 했던) 스크립트 입니다. 하지만 이 스크립트는 잘못 작성되었습니다.
-
파일1
잘못된 PIPESTATUS 사용 예(list2.sh)#!/bin/bash ./script.sh | ./sort-data.sh | ./calc.sh > output.txt echo ${PIPESTATUS[0]} echo ${PIPESTATUS[1]} #---------A echo ${PIPESTATUS[2]} #---------B
이걸 실행하면 A
와 B
의 echo 명령어에 아무것도 표시되지 않습니다.
-
실행 결과
$ ./list2.sh 0
이것은 파이프를 사용하지 않는 명령어 처리에서도 변수 PIPESTATUS는 0개의 파이프 처리로 보고 늘 변경되기 때문입니다. 따라서
A
에서는 직전의 echo 명령어의 두 번째 파이프 처리 종료 스테이터스를 얻으려고 하는데 그런 처리가 존재하지 않으므로 공백문자열을 출력합니다.
B
도 마찬가지입니다. 이렇듯 어떤 명령어를 실행할 때마다 PIPESTATUS는 덮어쓰기로 변경됩니다. 따라서 예제에서는 PIPESTATUS를 일단 셸 변수 pipe_status에 그대로 복사합니다.
주의사항
-
셸 변수 PIPESTATUS는 sh에는 없는 bash 고유 셸 변수 입니다. 그 외에도 몇 가지 bash 고유 셸 변수가 있는데 그 중 몇가지를 소개합니다.
-
bash 고유의 편리한 셸 변수
셸 변수명 의미 BASH 현재 실행하고 있는 bash를 실행할 때의 전체 경로 DIRSTACK 디렉터리 스택 내용(배열 변수) SHEELOPTS 유효한 셸 옵션(쉼표로 구분) SECONDS 셸이 실행된 초 HOSTNAME 호스트명 UID 셸을 실행한 사용자 ID GROUPS 현재 사용자가 속한 그룹 목록(배열 변수) 그 외에도 다양한 bash 셸 변수가 있으므로 man bash로 확인해보기 바랍니다.