commandA
다음 인쇄를 수행하고 싶습니다 .
line1
line2
line3
started successfully
line4
...
...
해당 줄이 인쇄 되면 started successfully
다른 명령을 시작하고 싶습니다(그리고 출력을 다중화하여 둘 다 터미널에 인쇄되도록 할 수도 있습니다).
Bash에서 어떻게 이를 달성할 수 있나요?
답변1
다음 방법 중 하나로 이 작업을 수행할 수 있습니다.
$ outputfile=output.log
$ commandA | tee "$outputfile" & tail --pid=$! -n+1 -f "$outputfile" |grep -q "started successfully" && commandB; wait
설명하다:
- 이
tee
명령을 사용하여 STDOUT 및$outputfile
. - 백그라운드에서 명령을 실행하면 이제 출력을 추적할 수 있습니다.
tail --pid=$! -n+1 -f "$outputfile"
tail
- 명령을 실행$outputfile
하고 첫 번째 줄부터 출력을 추적합니다.- 이 플래그는 명령이 완료되면 종료되도록
--pid=$!
하는 데 필요합니다 .tail
commandA
- 이 플래그는 명령이 완료되면 종료되도록
grep -q "started successfully"
- 필요한 문자열이 처음 나타날 때 성공적으로(조용히) 종료됩니다.&& commandB
-grep
문자열이 발견되면 실행됩니다commandB
.- 실행을 보장하는
&&
대신 사용해야 합니다.;
commandB
오직 의 출력에서 문자열이 발견되면commandA
. 이 문자열을 쓰지 않고 완료되는 경우commandA
(예: 실패하는 경우) 두 번째 명령을 실행하고 싶지 않습니다.
- 실행을 보장하는
wait
- 이전에 완료된 경우commandB
셸로 돌아가기 전에commandA
실행이 계속될 때까지 기다립니다 .commandA
- 또는 이전에 완료된 경우 포그라운드에서 계속 실행 하는
fg
대신 실행할 수 있습니다.wait
commandA
commandB
- 또는 이전에 완료된 경우 포그라운드에서 계속 실행 하는
고쳐 쓰다
tail
@PaulPazderski의 답변에서 영감을 받아 로그 파일과 명령이 없는 또 다른 방법이 있습니다 .
$ commandA | tee >(grep -q "started successfully" && commandB; cat >/dev/null)
유일한 단점은 commandA
완료 되면앞으로 commandB
, 쉘은 완료되자마자 프롬프트로 돌아가며 commandA
, commandB
백그라운드에서 계속 실행되고 터미널에 출력을 쓸 수도 있습니다.
이 문제를 해결하려면 최종 출력을 다른 명령으로 파이프하면 됩니다. 예를 들어 cat
stdout에 아무 것도 기록되지 않은 경우에만 쉘이 프롬프트로 돌아가도록 합니다(이는 전체 명령이 완료되었음을 의미함 commandB
).
$ commandA | tee >(grep -q "started successfully" && commandB; cat >/dev/null) | cat
답변2
다음과 같은 "트랙 출력" 스크립트를 사용해 보겠습니다.
#!/bin/bash
while IFS= read -r line; do
echo "$line"
if [[ "$line" == "started successfully" ]]; then
do_something
# optional exit but that could disrupt commandA with a SIGPIPE
fi
done
그리고 전화해
commandA | track-output
또는 tee
대체를 사용하고 처리하며 스크립트에 에코 라인이 없는 경우
commandA | tee >(track-output)
답변3
가능한:
{
commandA 3<&- | perl -pe '
exec "commandB <&3 3<&-" if !$pid && /started successfully/ && ($pid = fork) == 0;
END {if ($pid) {wait; exit($? & 127 ? $? + 128 : $? >> 8)}}'
} 3<&0
bash 및 GNU 대신 zsh를 사용하는 경우 tee
비슷한 작업을 수행할 수 있지만 commandB
종료 상태가 손실된다는 경고가 표시됩니다.
{
commandA 3<&- |
tee -p >(grep -q 'started successfully' && commandB <&3 3<&-)
} 3<&0
이는 대기 시간이 없다는 점을 제외하면 bash에서도 작동합니다(항상 포크를 최적화하지 않는 exec
앞에 commandB
as를 추가 해야 하지만 ).bash
commandB
두 가지 방법 모두 및 의 표준 입력이 방해받지 않도록 commandA
주의 합니다 . stdout은 원본에서 파이프로 변경됩니다. 이는 파이프를 살펴봐야 하는 경우 필요하지만 해당 출력은 또는 에 의해 전달 됩니다 . 표준 출력은 중단되지 않습니다.commandB
commandA
started successfully
perl -p
tee
commandB
일부 명령에 주의하세요완충기stdout이 터미널 장치가 아닐 때의 출력입니다. 따라서 commandA
여기의 출력은 다음과 같습니다.관로started successfully
, 큰 청크의 일부로만 인쇄되고 commandB
가능한 한 빨리 시작되지 않을 수 있습니다.
다음을 사용하여 해당 출력을 의사 터미널에 연결하기 시작 zsh
하면 이 문제를 해결할 수 있습니다 .zpty
commandA
zmodload zsh/zpty
# start commandA with only stdout going to the pseudo-terminal
# stdin and stderr restored.
zpty A 'command A <&3 3<&- 2>&4 4>&-' 3<&0 4>&2
pid=
while zpty -r A line; do
print -rn - $line
if (( ! pid )) && [[ $line = *"started successfully" ]]; then
{ commandB <&3 3<&- & } 3<&0
pid=$!
fi
done
(( ! pid )) || wait $pid
답변4
아래 코드는 "Some text"를 인쇄한 command2
후 실행됩니다 command1
(일치하는 grep이 종료되므로). 그 후 출력 command1
은 command2
표준 출력으로 인쇄됩니다.
command1 | tee >(grep -o -m1 "Some text"; command2 )