파이프 출력 및 캡처 종료 상태(Bash)
Bash에서 롱런 명령어를 실행하여 둘 다 종료 상태를 캡처하고 출력을 티합니다.
그래서 이렇게 하죠.
command | tee out.txt
ST=$?
가 종료 입니다.tee
령권이이없없없떻게하 하결 ?결? ????
명령어가 장시간 실행 중이고 나중에 보기 위해 출력을 파일로 리디렉션하는 것은 좋은 솔루션이 아닙니다.
에는 "Bash"라는 이름이 "Bash"입니다.$PIPESTATUS
; 명령어의 마지막 포그라운드 파이프라인에서 각 명령어의 종료 상태를 유지하는 배열입니다.
<command> | tee out.txt ; test ${PIPESTATUS[0]} -eq 0
또는 다른 셸(zsh 등)에서도 사용할 수 있는 또 다른 대안은 파이프 장애를 활성화하는 것입니다.
set -o pipefail
...
첫 번째 옵션은 와 함께 사용할 수 없습니다.zsh
구문이 조금 다르기 때문에.
멍청한 솔루션:명명된 파이프(mkfifo)를 통해 연결합니다.그런 다음 명령어를 두 번째로 실행할 수 있습니다.
mkfifo pipe
tee out.txt < pipe &
command > pipe
echo $?
의 bash 사용set -o pipefail
이 되다
pipefail: 파이프라인의 반환값은 0이 아닌 상태로 종료된 마지막 명령어 상태 또는 0이 아닌 상태로 종료된 명령어가 없는 경우 0입니다.
파이프에 있는 각 명령의 종료 상태를 알려주는 배열이 있습니다.
$ cat x| sed 's///'
cat: x: No such file or directory
$ echo $?
0
$ cat x| sed 's///'
cat: x: No such file or directory
$ echo ${PIPESTATUS[*]}
1 0
$ touch x
$ cat x| sed 's'
sed: 1: "s": substitute pattern can not be delimited by newline or backslash
$ echo ${PIPESTATUS[*]}
0 1
이 솔루션은 bash 고유의 기능이나 임시 파일을 사용하지 않아도 동작합니다.보너스: 결국 종료 상태는 파일 내의 일부 문자열이 아닌 종료 상태입니다.
상황:
someprog | filter
가 「」로부터 하게 됩니다.someprog
" "에서 합니다.filter
.
저의 솔루션은 다음과 같습니다.
((((someprog; echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1
echo $?
자세한 설명과 서브셸 및 경고 없는 대체 방법에 대해서는 unix.stackexchange.com에서 같은 질문에 대한 답변을 참조하십시오.
합하여를 PIPESTATUS[0]
및 실행 결과exit
서브셸에서는 첫 번째 명령어 반환값에 직접 액세스할 수 있습니다.
command | tee ; ( exit ${PIPESTATUS[0]} )
다음은 예를 제시하겠습니다.
# the "false" shell built-in command returns 1
false | tee ; ( exit ${PIPESTATUS[0]} )
echo "return value: $?"
다음과 같은 것이 있습니다.
return value: 1
그래서 저는 레즈마나와 같은 답변을 드리고 싶었지만, 제 답변이 좀 더 간단하고 조금 더 유리하다고 생각합니다.
# You want to pipe command1 through command2:
exec 4>&1
exitstatus=`{ { command1; printf $? 1>&3; } | command2 1>&4; } 3>&1`
# $exitstatus now has command1's exit status.
이것은 inside out에서 설명하는 것이 가장 좋다고 생각합니다.command1은 stdout(파일 기술자 1)에서 일반 출력을 실행하고 인쇄합니다.그 후 printf는 icommand1의 종료 코드를 stdout에서 실행 및 인쇄합니다.단, stdout은 파일 기술자 3으로 리다이렉트 됩니다.
command1이 실행 중일 때 stdout은 command2로 파이핑됩니다(printf의 출력에서는 command2가 되지 않습니다.이것은 파이프에 읽히는 1이 아닌 파일 기술자3에 송신되기 때문입니다).그런 다음 명령어2의 출력을 파일 기술자4로 리다이렉트하여 파일 기술자1에서 벗어나게 합니다.파일 기술자1의 빈 공간을 잠시 확보합니다.파일 기술자3의 printf 출력을 파일 기술자1로 되돌리기 위해서입니다.이것이 명령어 대체(백틱)입니다.캡처하고 변수에 배치하는 것입니다.
마법의 한 은 첫 번째 입니다.exec 4>&1
이 명령어는 별도의 명령어로 실행되었으며 외부 셸의 stdout 복사본으로 파일 기술자 4가 열립니다.그 안에 되어 있는 것이 명령어 한 가 .명령어 치환에서는 캡처되지 않지만 명령어 치환에서 "아웃"된 후에도 사실상 스크립트의 전체 파일 기술자 1로 이동합니다.
(the)exec 4>&1
을 하다대부분의 일반적인 셸이 명령어 대체 내의 파일 기술자에 쓰려고 할 때 이를 좋아하지 않기 때문입니다.불복하다이것이 가장 간단한 휴대용 방법입니다.)
명령어 출력이 서로 오버하는 것처럼 기술적이고 장난스러운 방법으로 볼 수 있습니다.command1은 command2로 파이프 접속하고 printf의 출력은 command2가 command2를 잡지 않도록 명령어2를 넘어갑니다.다음으로 명령어2의 출력은 printf가 치환에 의해 캡처되는 타이밍에 명령어2의 출력이 점프를 반복하고 명령어2의 출력은 일반 파이프와 마찬가지로 표준 출력에 쓰이고 있습니다.
그리고 제가 알기로는$?
는 파이프에 두 번째 명령어 반환 코드를 계속 포함합니다.변수 할당, 명령어 대체 및 복합 명령어는 모두 명령어 반환 코드에 효과적으로 투과되기 때문에 command2의 반환 상태는 전파됩니다.레즈마나가 이이 더 합니다.이게 레즈마나가 제안한 해결책보다 더 나은 해결책이라고 생각하는 이유입니다.
lesmana가 언급한 경고에 따르면 command1은 어느 시점에서 파일 기술자 3 또는4를 사용하게 될 가능성이 있기 때문에 보다 견고하게 하기 위해 다음과 같이 합니다.
exec 4>&1
exitstatus=`{ { command1 3>&-; printf $? 1>&3; } 4>&- | command2 1>&4; } 3>&1`
exec 4>&-
예에서는 를 사용하고 (subshells 사용)은 다음과 .( )
{ }
효율이 동작합니다).
파일 에서 파일 기술자를 에 두 줄 명령어가 이어집니다.4 .3>&1
3번입니다. 그...4>&-
inner compound 4를 .3>&-
는 파일 기술자 3을 상속하지 않으므로 command1은 보다 표준적인 '확장' 환경을 얻습니다.도 있어요.4>&-
3>&-
하지만 가능한 한 범위를 제한하지 않을 수 없습니다.
파일 기술자 3과 파일 기술자 4를 직접 사용하는 빈도가 얼마나 되는지는 잘 모르겠습니다.대부분의 프로그램에서는 사용되지 않는 파일 기술자를 반환하는 시스템 콜을 사용하고 있습니다만, 때때로 파일 기술자 3에 직접 코드를 쓰기도 합니다(파일 기술자가 열려 있는지 확인하고, 열려 있으면 사용하는 것을 생각할 수 있습니다).또는 그렇지 않은 경우 그에 따라 다르게 행동한다.따라서 후자는 아마도 염두에 두고 범용 케이스에 사용하는 것이 가장 좋을 것입니다.
(command | tee out.txt; exit ${PIPESTATUS[0]})
@cODAR의 응답과 달리 이 명령어는 첫 번째 명령어의 원래 종료 코드를 반환합니다.0, 127로 하다 @Chaoran으로 전화하면 .${PIPESTATUS[0]}
단, 모든 것을 괄호로 묶는 것이 중요합니다.
에서는 Ubuntu deb Debian을 사용할 수 .apt-get install moreutils
이에는 " " 라는 가 포함되어 있습니다.mispipe
파이프 내의 첫 번째 명령어 종료 상태를 반환합니다.
bash 이외에서는 다음을 수행할 수 있습니다.
bash -o pipefail -c "command1 | tee output"
이 닌자일 합니다./bin/sh
.
플레인 bash에서 이를 수행하는 가장 간단한 방법은 파이프라인 대신 프로세스 치환을 사용하는 것입니다.몇 가지 차이점이 있지만 사용 사례에는 크게 문제가 되지 않을 수 있습니다.
- 파이프라인을 실행할 때 bash는 모든 프로세스가 완료될 때까지 기다립니다.
- Ctrl+C를 bash로 전송하면 메인 프로세스뿐만 아니라 파이프라인의 모든 프로세스가 중지됩니다.
pipefail
과 " " "PIPESTATUS
변수는 프로세스 대체와 무관합니다.- 그 이상일 가능성이 있다
치환을 bash에는 .그것은 에도 표시되지 않습니다.jobs
.
은 차치하고, 「 」 「 」 「 」 「 」consumer < <(producer)
★★★★★★★★★★★★★★★★★」producer | consumer
기본적으로 동등합니다.
와 대체 .producer > >(consumer)
의 경우 다음 중 하나:
command > >(tee out.txt)
예:
$ { echo "hello world"; false; } > >(tee out.txt)
hello world
$ echo $?
1
$ cat out.txt
hello world
$ echo "hello world" > >(tee out.txt)
hello world
$ echo $?
0
$ cat out.txt
hello world
말했듯이 파이프 표현과는 차이가 있습니다.파이프 닫힘에 민감하지 않으면 프로세스가 실행을 중지하지 않을 수 있습니다.특히, 그것은 당신의 stdout에 무언가를 계속 쓸 수 있고, 이것은 혼란스러울 수 있습니다.
PIPESTATUS[@]는 pip 명령이 반환된 직후에 배열에 복사해야 합니다.PIPESTATUS[@]를 읽으면 내용이 지워집니다.모든 파이프 명령의 상태를 확인할 예정이라면 다른 어레이에 복사하십시오.$?"는 ${PIPESTATUS[@]}의 마지막 요소와 동일한 값이며, "${PIPESTATUS[@]}"을 읽으면 "${PIPESTATUS[@]}"가 파괴되는 것 같지만 완전히 검증되지는 않았습니다.
declare -a PSA
cmd1 | cmd2 | cmd3
PSA=( "${PIPESTATUS[@]}" )
파이프가 하위 쉘에 있는 경우 이 작업은 작동하지 않습니다.는, 「 」를 참조해 주세요.
backticked 명령어로 bash pipestatus를 참조하십시오.
@brian-s-wilson의 답변을 기반으로 합니다.이 bash 도우미 기능은 다음과 같습니다.
pipestatus() {
local S=("${PIPESTATUS[@]}")
if test -n "$*"
then test "$*" = "${S[*]}"
else ! [[ "${S[@]}" =~ [^0\ ] ]]
fi
}
다음과 같이 사용됩니다.
1: get_bad_things는 성공해야 하지만 출력은 생성되지 않습니다.단, 출력은 생성되어야 합니다.
get_bad_things | grep '^'
pipeinfo 0 1 || return
2: 모든 파이프라인이 성공해야 합니다.
thing | something -q | thingy
pipeinfo || return
순수 쉘 솔루션:
% rm -f error.flag; echo hello world \
| (cat || echo "First command failed: $?" >> error.flag) \
| (cat || echo "Second command failed: $?" >> error.flag) \
| (cat || echo "Third command failed: $?" >> error.flag) \
; test -s error.flag && (echo Some command failed: ; cat error.flag)
hello world
, 이제 두 입니다.cat
replaced replaced false
:
% rm -f error.flag; echo hello world \
| (cat || echo "First command failed: $?" >> error.flag) \
| (false || echo "Second command failed: $?" >> error.flag) \
| (cat || echo "Third command failed: $?" >> error.flag) \
; test -s error.flag && (echo Some command failed: ; cat error.flag)
Some command failed:
Second command failed: 1
First command failed: 141
첫 번째 고양이도 고장 나기 때문에 주의해 주세요.로그에서 실패한 명령어의 순서는 이 예에서 올바르지만, 이 순서에 의존하지 마십시오.
이 방법을 사용하면 각 명령어의 stdout 및 stderr을 캡처할 수 있습니다.이것에 의해, 에러가 발생했을 경우 로그 파일에 덤프 하거나, 에러가 발생하지 않았을 경우(dd 출력 등) 삭제하거나 할 수 있습니다.
bash.pipeline은 최소 프로세스스크립트 언어 execline에서 두 번째 명령어*의 반환 코드를 사용하여 종료됩니다.sh
하지만, '''와 달리'sh
로 할 수 할 수 는 모두 ' 반환 코드'에sh
라인(단, 「」)에서는,execline
스스:::: 。
$ # using the full execline grammar with the execlineb parser:
$ execlineb -c 'pipeline { echo "hello world" } tee out.txt'
hello world
$ cat out.txt
hello world
$ # for these simple examples, one can forego the parser and just use "" as a separator
$ # traditional order
$ pipeline echo "hello world" "" tee out.txt
hello world
$ # "write" order (second command writes rather than reads)
$ pipeline -w tee out.txt "" echo "hello world"
hello world
$ # pipeline execs into the second command, so that's the RC we get
$ pipeline -w tee out.txt "" false; echo $?
1
$ pipeline -w tee out.txt "" true; echo $?
0
$ # output and exit status
$ pipeline -w tee out.txt "" sh -c "echo 'hello world'; exit 42"; echo "RC: $?"
hello world
RC: 42
$ cat out.txt
hello world
「」를 사용합니다.pipeline
는 네이티브 bash 파이프라인과 응답 #43972501에서 사용되는 bash 프로세스 치환과 같은 차이가 있습니다.
* 실실pipeline
에러가 없는 한, 는 전혀 종료하지 않습니다.이 명령어는 두 번째 명령어로 실행되므로 반환을 실행하는 두 번째 명령어입니다.
'우리'를 쓰지 거죠?stderr
★★★★★★★★★★★★★★★★★★?
(
# Our long-running process that exits abnormally
( for i in {1..100} ; do echo ploop ; sleep 0.5 ; done ; exit 5 )
echo $? 1>&2 # We pass the exit status of our long-running process to stderr (fd 2).
) | tee ploop.out
★★★★★★★★★★★★★★★★★.ploop.out
을 받다stdout
stderr
는 롱런 프로세스의 종료 상태를 수신합니다.포식스
(장기 실행 프로세스의 범위 표현식을 제외하고, 실제로는 관련이 없습니다).
다음은 예를 제시하겠습니다.
...
ploop
ploop
ploop
ploop
ploop
ploop
ploop
ploop
ploop
ploop
5
코드 「」가 것에 해 주세요.5
는, 파일의 .ploop.out
.
언급URL : https://stackoverflow.com/questions/1221833/pipe-output-and-capture-exit-status-in-bash
'programing' 카테고리의 다른 글
파라미터의 큰따옴표 이스케이프 (0) | 2023.04.08 |
---|---|
무제한 Bash 이력 (0) | 2023.04.08 |
대상 시스템에서 백그라운드에서 명령을 실행하도록 ssh를 가져오는 중 (0) | 2023.04.08 |
한 셸 스크립트에서 다른 셸 스크립트로 모든 변수를 전달하시겠습니까? (0) | 2023.04.08 |
Bash 스크립트를 사용하여 사용자 계정과 비밀번호를 자동으로 추가하는 방법은 무엇입니까? (0) | 2023.04.08 |