Bash 在Linux shell中匹配连续流中的一行

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

如何在匹配第一行后立即退出以下命令?我知道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 "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
ing
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
杀死
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而自动退出。