종료 코드의 결과에 따라 명령의 표준 출력을 파이프하는 방법

종료 코드의 결과에 따라 명령의 표준 출력을 파이프하는 방법

다음에서 확장됨이 문제, 명령의 성공 여부에 따라 명령의 표준 출력을 파이프하려는 사용 사례가 있습니다.

기본 파이프라인으로 시작합니다.

command | grep -P "foo"

command그러나 때로는 표준 출력에 아무 것도 인쇄되지 않지만 실제로는 종료 코드가 있는 것을 확인했습니다 . 우리는 이 상황을 무시하고 종료 코드가 다음과 같은 경우에만 오류 코드를 출력하기를 0원합니다.grep1

실제 예를 들어 다음 명령을 구현할 수 있습니다.

OUTPUT=$(command) # exit code is 0 or 1
RESULT=$?
if [ $RESULT -eq 0 ]; then
  return $RESULT; # if the exit code is 0 then we simply pass it forward
else
  grep -P "foo" <<< $OUTPUT; # otherwise check if the stdout contains "foo"
fi

하지만 여기에는 스크립트를 작성해야 한다는 단점이 많이 있습니다. 즉, 콘솔에서만 실행할 수는 없다는 의미입니다. 이것도 좀 아마추어적인 것 같습니다.

1더 깔끔한 구문을 위해 종료 코드가 이면 종료 코드를 앞으로 파이프하고, 그렇지 않으면 종료 코드를 앞으로 파이프 하는 가상의 삼항 연산자를 상상해 봅니다 .

command |?1 grep -P "foo" : $?

이 결과를 달성할 수 있는 일련의 운영자 및 유틸리티가 있습니까?

답변1

파이프라인의 명령은 동시에 실행됩니다. 이는 파이프 및 프로세스 간 통신 메커니즘의 전부입니다.

존재하다:

cmd1 | cmd2

cmd1cmd2동시에 cmd2기록된 데이터를 처리하기 시작 합니다 cmd1.

cmd2실패 시에만 시작 하려는 경우 cmd1종료 상태를 보고한 cmd2후 시작 해야 하므로 cmd1파이프를 사용할 수 없으며 cmd1생성된 모든 데이터를 보관하기 위해 임시 파일을 사용해야 합니다.

 cmd1 > file || cmd2 < file; rm -f file

또는 귀하의 예와 같이 메모리에 저장하지만 여기에는 다른 많은 문제가 있습니다(예: $(...)후행 줄 바꿈을 모두 제거하고 대부분의 쉘은 큰 출력에 대한 크기 조정 문제는 말할 것도 없고 NUL 바이트를 처리할 수 없습니다).

zshLinux에서는 또는 같은 셸을 사용하여 bash여기에 문서를 저장하고 여기에 문자열을 임시 파일에 저장하려면 다음을 수행할 수 있습니다.

{ cmd1 > /dev/fd/3 || cmd2 <&3 3<&-; } 3<<< ignored

쉘이 임시 파일의 생성 및 정리를 처리하도록 합니다.

bash 버전 5는 이제 임시 파일을 생성한 후 해당 파일에 대한 쓰기 권한을 제거하므로 위의 방법은 작동하지 않습니다. 이 문제를 해결하려면 먼저 쓰기 권한을 복원해야 합니다.

{ chmod u+w /dev/fd/3
  cmd1 > /dev/fd/3 || cmd2 <&3 3<&-; } 3<<< ignored

수동으로, POSIX적으로:

tmpfile=$(
  echo 'mkstemp(template)' |
    m4 -D template="${TMPDIR:-/tmp}/XXXXXX"
) && [ -n "$tmpfile" ] && (
  rm -f -- "$tmpfile" || exit
  cmd1 >&3 3>&- 4<&- ||
    cmd2 <&4 4<&- 3>&-) 3> "$tmpfile" 4< "$tmpfile"

mktemp일부 시스템 에는 임시 파일 생성을 좀 더 쉽게 해주는 비표준 명령(시스템마다 인터페이스가 다르지만)이 있습니다 ( tmpfile=$(mktemp)일부 시스템에서는 파일을 생성하지 않으므로 조정이 필요할 수 있지만 대부분의 구현에는 충분해야 합니다 umask). . 이는 호환 가능한 구현에는 [ -n "$tmpfile" ]필요하지 않지만 GNU는 호출이 실패할 때 0이 아닌 종료 상태를 반환하지 않기 m4때문에 적어도 호환되지 않습니다 .m4mkstemp()

또한 코드 실행을 막을 수 있는 것은 아무것도 없습니다.편안. 당신의"스크립트"이는 대화형 셸의 프롬프트에서 동일한 방식으로 입력할 수 있지만( return코드가 함수에 있다고 가정되는 부분 제외) 다음과 같이 단순화할 수 있습니다.

output=$(cmd) || grep foo <<< "$output"

답변2

참고: 질문에 태그가 지정되었으므로, bash 기능을 사용할 수 있다고 가정합니다.

예제 코드를 보면 다음과 같은 작업을 수행하려는 것 같습니다.

  • 명령의 종료 상태가 0이면 명령의 종료 상태가 사용됩니다.
  • grep그렇지 않으면 종료 상태를 사용하십시오 .

빈 파이프에서 실행하면 grep비용이 전혀 들지 않으므로 어쨌든 파이프를 사용하여 명령의 종료 상태를 확인하는 것이 좋습니다. PIPESTATUS배열을 사용하여 bash에서 얻을 수 있습니다.

$ (echo foo; exit 1) | grep foo
foo
$ echo "${PIPESTATUS[@]}"
1 0  

여기서 서브쉘은 1로 종료되고 grep은 0으로 종료됩니다.

그래서 당신은 이것을 할 수 있습니다 :

(command | grep -P "foo"; exit "$((PIPESTATUS[0] ? $? : 0))")

표현은 단순화될 수 있지만 아이디어를 얻을 수 있습니다.


단순히 출력을 가짜로 만드는 옵션도 있습니다.

(command && echo foo) | grep -P foo

echo foowhere는 명령이 성공한 경우에만 실행되므로 grep도 성공할 수 있습니다.

관련 정보