데몬을 백그라운드로 보내고 데몬이 다음 줄을 포함하여 특정 줄을 stderr에 출력하는 경우에만 스크립트 실행을 계속하고 싶습니다.
# Fictional daemon
{
for x in {1..9}; do
sleep 1
if [ "$x" != "5" ]; then
echo $x 1>&2
else
echo now 1>&2
fi
done
} &
# Daemon PID
PID="$!"
# Wait until the particular output...
until { read -r line && grep -q "now" <<<"$line"; } do
sleep 1
done </proc/$PID/fd/2
#
# Do more stuff...
#
fg
답변1
그 mydaemon
동작은 다음과 같습니다:
#! /bin/sh -
i=1 stage=init
while true; do
if [ "$i" -eq 5 ]; then
echo >&2 ready
stage=working
fi
echo >&2 "$stage $i"
sleep 1
i=$((i + 1))
done
즉, 초기화하고, ready
준비되면 출력하고, 작업을 계속하는 데 4초가 소요됩니다. 모두 동일한 프로세스에서 start-mydaemon
다음과 같은 스크립트를 작성할 수 있습니다.
#! /bin/sh -
DAEMON=./mydaemon
LOGFILE=mydaemon.log
umask 027
: >> "$LOGFILE" # ensures it exists
pid=$(
sh -c 'echo "$$"; exec "$0" "$@"' tail -fn0 -- "$LOGFILE" | {
IFS= read -r tail_pid
export LOGFILE DAEMON
setsid -f sh -c '
echo "$$"
exec "$DAEMON" < /dev/null >> "$LOGFILE" 2>&1'
grep -q ready
kill -s PIPE "$tail_pid"
}
)
printf '%s\n' "$DAEMON started in process $pid and now ready"
$ time ./start-mydaemon
./mydaemon started in process 230254 and now ready
./start-mydaemon 0.01s user 0.01s system 0% cpu 4.029 total
$ ps -fjC mydaemon
UID PID PPID PGID SID C STIME TTY TIME CMD
chazelas 230254 6175 230254 230254 0 10:28 ? 00:00:00 /bin/sh - ./mydaemon
start-mydaemon
mydaemon
준비가 되었음을 표시할 때까지 반환되지 않습니다. 여기서 mydaemon
stdout 및 stderr은 로그 파일로 이동하고, stdin은 /dev/null
. setsid -f
(표준 명령은 아니지만 대부분의 Linux 배포판에서 사용 가능)에서 데몬이 터미널에서 분리되었는지 확인해야 합니다(터미널에서 시작된 경우).
mydaemon
그러나 초기화가 실패하고 를 쓰지 않고 종료 되면 ready
스크립트는 ready
결코 오지 않을(또는 mydaemon
다음에 성공적으로 시작할 때 올) 무언가를 영원히 기다릴 것이라는 점에 유의하십시오 .
또한 sh -c ...tail
및 데몬이 동시에 시작된다는 점에 유의하십시오. 초기화하고 처음부터 인쇄하여 mydaemon
로그 파일의 끝을 찾으면 메시지가 누락됩니다.ready
tail
tail
ready
다음을 통해 이러한 문제를 해결할 수 있습니다.
#! /bin/sh -
DAEMON=./mydaemon
LOGFILE=mydaemon.log
export DAEMON LOGFILE
umask 027
died=false ready=false canary_pid= tail_pid= daemon_pid=
: >> "$LOGFILE" # ensures it exists
{
exec 3>&1
{
tail -c1 <&5 5<&- > /dev/null # skip to the end synchronously
(
sh -c 'echo "tail_pid=$$" >&3; exec tail -fn+1' |
{ grep -q ready && echo ready=true; }
) <&5 5<&- &
} 5< "$LOGFILE"
setsid -f sh -c '
echo "daemon_pid=$$" >&3
exec "$DAEMON" < /dev/null 3>&- 4>&1 >> "$LOGFILE" 2>&1' 4>&1 |
(read anything; echo died=true) &
echo "canary_pid=$!"
} | {
while
IFS= read -r line &&
eval "$line" &&
! "$died" &&
! { [ -n "$daemon_pid" ] && "$ready" ; }
do
continue
done
if "$ready"; then
printf '%s\n' "$DAEMON started in process $daemon_pid and now ready"
else
printf >&2 '%s\n' "$DAEMON failed to start"
fi
kill -s PIPE "$tail_pid" "$canary_pid" 2> /dev/null
"$ready"
}
하지만 이것은 꽤 복잡해지기 시작했습니다. 또한 tail -f
이제 Linux의 stdin에서 실행되므로 파일에 새 데이터가 있는지 감지하기 위해 inotify를 사용하지 않고 일반적인 방법을 사용합니다.매초마다 확인하세요ready
이는 로그 파일에서 감지하는 데 추가 시간이 걸릴 수 있음을 의미합니다 .
답변2
... done </proc/$PID/fd/2
이게 생각보다 잘 안 되더라구요.
표준 오류 $PID
는
- tty를 제어합니다. 이 경우 사용자가 입력한 문자열을 읽으려고 합니다.표준 입력~의
$PID
- 파이프 - 당신은 이미 그것을 읽고 있는 사람들과 경쟁하게 될 것이고, 이는 완전한 혼란으로 이어질 것입니다.
/dev/null
——EOF!- 다른 건 없나요 ;-)?
실행 중인 프로세스에서 다른 곳으로 파일 설명자를 리디렉션하는 방법은 몇 가지뿐이므로 가장 좋은 방법은 입력 대기 코드가 cat >/dev/null
백그라운드에서 실행되도록 수준을 낮추는 것입니다.
예를 들어, 데몬이 출력할 때까지 "대기"합니다 4
.
% cat /tmp/daemon
#! /bin/sh
while sleep 1; do echo $((i=i+1)) >&2; done
% (/tmp/daemon 2>&1 &) | (sed /4/q; cat >/dev/null &)
1
2
3
4
%
그러면 쉘 제어 없이 /tmp/daemon
쓰기가 계속됩니다.cat >/dev/null &
또 다른 해결책은 데몬의 stderr를 일반 파일로 리디렉션하는 것입니다 tail -f
. 그러나 데몬은 디스크를 계속 쓰레기로 채울 것입니다( rm
파일이 있더라도 데몬이 종료할 때까지 차지하는 공간은 해제되지 않습니다). 자원고갈 상황보다 더 나쁘다 cat
.
물론 가장 좋은 방법은 /tmp/daemon
초기화 후 백그라운드로 들어가 표준 파일 설명자를 닫고 syslog(3)
오류를 인쇄하는 등 의 실제 데몬을 작성하는 것입니다.
답변3
다른 답변에는 파일이나 명명된 파이프 사용이 포함됩니다. 너도 그렇지 않아. 간단한 파이프라인이면 충분합니다.
#!/bin/bash
run_daemon() {
# Fictional daemon
{
for x in {1..9}; do
sleep 1
if [ "$x" != "5" ]; then
echo $x 1>&2
else
echo now 1>&2
fi
done
}
}
run_daemon 2>&1 | (
exec 9<&0 0</dev/null
while read -u 9 line && test "$line" != now
do
echo "$line" ## or ## :
done
echo "$line" ## or ##
cat /dev/fd/9 & ## or ## cat /dev/fd/9 >/dev/null &
#fictional stuff
for a in 1 2 3
do
echo do_stuff $a
sleep 1
done
echo done
wait
)
## or ##
혼합 데몬의 출력을 표시하지 않으려면 표시된 섹션이 대안입니다. cat은 데몬이 출력 시 SIGPIPE를 가져오는 것을 방지하는 유일한 방법이므로 생략할 수 없습니다.
입력을 다른 파일 설명자( exec
)로 리디렉션하지 않으려고 했지만 이것이 없으면 입력이 닫히고 데몬이 종료됩니다.
또한 제가 명시적으로 데몬을 배경으로 설정하지 않았다는 점을 아실 수도 있습니다. 파이프를 사용하면 그럴 필요가 없습니다. wait
데몬이 아닌 고양이가 기다리고 있습니다. 데몬이 백그라운드에 있으면 계속 작동합니다.
wait
와 유사 fg
하지만 콘솔 제어를 활성화하지 않습니다. (또한 훨씬 더 오래되었습니다.)
답변4
mkfifo
필요한 것을 할 수 있습니다. 이 답변을 참조하세요:
https://stackoverflow.com/questions/43949166/reading-value-of-stdout-and-stderr
대신 FIFO를 사용하세요
기술적으로 /dev/stdout 및 /dev/stderr은 실제로 FIFO나 명명된 파이프가 아닌 파일 설명자입니다. 내 시스템에서는 실제로는 /dev/fd/1 및 /dev/fd/2에 대한 심볼릭 링크일 뿐입니다. 이러한 설명자는 일반적으로 TTY 또는 PTY에 연결됩니다. 따라서 원하는 방식으로 실제로 읽을 수는 없습니다.
이 코드는 필요한 작업을 수행합니다(처리 단계를 제거했습니다). 읽기 루프 중에는 절전 모드가 필요하지 않습니다. fifo에서 코드 읽기가 중지되면 데몬은 가득 찰 때까지 쓰기를 계속 시도하고 fifo에서 데이터를 읽을 때까지 데몬은 차단됩니다.
fifo="daemon_errors"
{
mkfifo $fifo
for x in {1..9}; do
sleep 1
if [ "$x" != "5" ]; then
echo $x 1>&2
else
echo now 1>&2
fi
done 2>$fifo
} &
sleep 1 # need to wait until the file is created
# Read all output
while read -r line; do
echo "just read: $line"
done < $fifo