Bash 在Linux shell中匹配连续流中的一行
如何在匹配第一行后立即退出以下命令?我知道SIGPIPE在下次尝试写入()之前不会被发送到Bash 在Linux shell中匹配连续流中的一行,bash,Bash,如何在匹配第一行后立即退出以下命令?我知道SIGPIPE在下次尝试写入()之前不会被发送到cat,但我不知道如何解决这个问题 cat <( echo -ne "asdf1\nzxcv1\nasdf2\n"; sleep 5; echo -ne "zxcv2\nasdf3\n" ) | grep --line-buffered zxcv | head --lines=1 cat <( echo -ne "asdf1\nzxcv1\nasdf2\n"; sleep 5; echo -ne
cat
,但我不知道如何解决这个问题
cat <( echo -ne "asdf1\nzxcv1\nasdf2\n"; sleep 5; echo -ne "zxcv2\nasdf3\n" ) | grep --line-buffered zxcv | head --lines=1
cat <( echo -ne "asdf1\nzxcv1\nasdf2\n"; sleep 5; echo -ne "zxcv2\nasdf3\n" ) | grep --max-count=1 zxcv
cat有趣的问题!我已经验证了打印第一行后,head
会死亡(已删除背景作业噪音):
乍一看,head
似乎没有发送SIGPIPE,但我从运行strace grep
中得到了冲突的信息:
$ (printf '%s\n' a b a; sleep 10; printf '%s\n' a) | strace grep --line-buffered a | head --lines=1
…
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=21950, si_uid=1000} ---
+++ killed by SIGPIPE +++
$ (printf '%s\n' a b a; sleep 10; printf '%s\n' a) | grep --line-buffered a | head --lines=1 & sleep 1; kill -PIPE $(pgrep grep); sleep 5; pstree $$
a
bash─┬─bash───sleep
└─pstree
…和kill
inggrep
:
$ (printf '%s\n' a b a; sleep 10; printf '%s\n' a) | strace grep --line-buffered a | head --lines=1
…
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=21950, si_uid=1000} ---
+++ killed by SIGPIPE +++
$ (printf '%s\n' a b a; sleep 10; printf '%s\n' a) | grep --line-buffered a | head --lines=1 & sleep 1; kill -PIPE $(pgrep grep); sleep 5; pstree $$
a
bash─┬─bash───sleep
└─pstree
杀死grep
然后sleep
修复了这个问题:
$ (printf '%s\n' a b a; sleep 10; printf '%s\n' a) | grep --line-buffered a | head --lines=1 & sleep 1; kill -PIPE $(pgrep grep); sleep 1; kill -PIPE $(pgrep sleep); sleep 5; pstree $$
a
bash───pstree
结论:WTF?我最终做了以下工作,以便能够在匹配行和超时后中断跟踪日志
#!/bin/sh
TOP_PID=$$
container_id="$1"
LOG_PATH=/opt/jboss/jboss-eap-6.2/standalone/log/server.log
await_startup () {
status=$(check_status)
follow_log --timeout $timeout &
local bgjob_pid; local bgjob_status;
bgjob_pid=$(jobs -p)
test -n "$bgjob_pid" || die "Could not start background job to follow log."
bgjob_status=true
while [ "$status" = "not started" ] && $bgjob_status; do
sleep 1s
status=$(check_status)
if kill -0 $bgjob_pid 2>/dev/null; then
bgjob_status=true
else
bgjob_status=false
fi
done
kill -KILL $bgjob_pid 2>/dev/null
}
follow_log () {
# argument parsing skipped...
docker exec $container_id timeout $timeout tail --follow=name ---disable-inotify --max-unchanged-stats=2 /$LOG_PATH
}
check_status () {
local line;
line=$(docker exec $container_id grep --extended-regexp --only-matching 'JBoss EAP .+ started.+in' /$LOG_PATH | tail --lines=1)
if [ -z "$line" ]; then
printf "not started"
elif printf "%s" "$line" | grep --quiet "with errors"; then
printf "started and unhealthy"
else
printf "healthy"
fi
}
die () {
test -n "$1" && printf "%s\n" "$1"
kill -s TERM $TOP_PID
return 1
} 1>&2
我使用tail来跟踪日志文件,cat+sleep很好地模拟了tail-f的行为,这也恰好更容易测试。我甚至不明白你想做什么,或者为什么。我相信只要grep试图向head写一些东西,内核就会向grep发送SIGPIPE,但这首先发生在文本制作者从睡眠中醒来并向grep写了一些东西之后。所以,如果您过早地杀死grep,它将不会得到SIGPIPE,但如果您等待制作人打印某些内容,您将看到grep由于SIGPIPE而自动退出。