다음에서 확장됨이 문제, 명령의 성공 여부에 따라 명령의 표준 출력을 파이프하려는 사용 사례가 있습니다.
기본 파이프라인으로 시작합니다.
command | grep -P "foo"
command
그러나 때로는 표준 출력에 아무 것도 인쇄되지 않지만 실제로는 종료 코드가 있는 것을 확인했습니다 . 우리는 이 상황을 무시하고 종료 코드가 다음과 같은 경우에만 오류 코드를 출력하기를 0
원합니다.grep
1
실제 예를 들어 다음 명령을 구현할 수 있습니다.
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
cmd1
cmd2
동시에 cmd2
기록된 데이터를 처리하기 시작 합니다 cmd1
.
cmd2
실패 시에만 시작 하려는 경우 cmd1
종료 상태를 보고한 cmd2
후 시작 해야 하므로 cmd1
파이프를 사용할 수 없으며 cmd1
생성된 모든 데이터를 보관하기 위해 임시 파일을 사용해야 합니다.
cmd1 > file || cmd2 < file; rm -f file
또는 귀하의 예와 같이 메모리에 저장하지만 여기에는 다른 많은 문제가 있습니다(예: $(...)
후행 줄 바꿈을 모두 제거하고 대부분의 쉘은 큰 출력에 대한 크기 조정 문제는 말할 것도 없고 NUL 바이트를 처리할 수 없습니다).
zsh
Linux에서는 또는 같은 셸을 사용하여 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
때문에 적어도 호환되지 않습니다 .m4
mkstemp()
또한 코드 실행을 막을 수 있는 것은 아무것도 없습니다.편안. 당신의"스크립트"이는 대화형 셸의 프롬프트에서 동일한 방식으로 입력할 수 있지만( 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 foo
where는 명령이 성공한 경우에만 실행되므로 grep도 성공할 수 있습니다.