grep 后的管道命令不起作用

grep 后的管道命令不起作用

我想监视一个文件。因此,我使用以下命令跟踪了一个文件,以每行执行一个脚本。

tail -3f logfile.log | grep "action.*add" | sed -u -e "s/^/'/" -e "s/$/'/" | xargs -L 1 -P 5 bash testscript.sh

但似乎脚本没有被执行。我观察到grep没有为下一个管道提供任何输入。

当我尝试时,

tail -3f logfile.log | grep "action.*add"

有效。但是,当给出下一个过滤器(如、sed等)时,它的工作效果并不像下面所示的那样。grepxargs

tail -3f /var/tmp/rabbitmq-tracing/logfile.log | grep "action.*add" | grep add 

请帮助我了解为什么会发生这种情况以及如何克服这个问题。


编辑1: 基本上像下面这样的东西应该可以工作,并且它之前就可以工作。困惑为什么它现在不起作用。

tail -f file.txt | grep something | grep something | grep something

编辑2: 第一个 grep 之后的输出行将是如下所示的 json 字符串。我想将此行作为 bash 脚本的输入(用单引号引起来)。

{"twNotif": {"originator": "api", "chain": "test", "txId": "08640-0050568a5514", "version": "1.0", "msgType": "api", "twData": {"api": {"hostId": "007bdcc5", "user": "test", "cmdTxt": "100599"}}, "action": "add", "store": "test", "msgTime": 1467280648.971042}}

答案1

使用--line-buffered开关grep

tail -3f logfile.log | grep --line-buffered "action.*add" | sed -u -e "s/^/'/" -e "s/$/'/" | xargs -L 1 -P 5 bash testscript.sh

来自 man grep:

--line-buffered 对输出使用行缓冲。这可能会导致性能损失。


或者你可以使用stdbuf 阅读更多

stdbuf 允许修改与程序关联的三个标准 I/O 流的缓冲操作。概要:

使用这个语法:

... | stdbuf -oL grep ... | ...

你的例子:

tail -3f logfile.log | stdbuf -oL grep "action.*add" | sed -u -e "s/^/'/" -e "s/$/'/" | xargs -L 1 -P 5 bash testscript.sh

答案2

正如其他人所指出的,grep的行缓冲是导致问题的明显原因。

然而,您正在做的事情还存在其他不那么明显的问题。

首先,您似乎正在使用sed在每个输出行的开头和结尾添加撇号,以便您可以将其输入到xargs.这甚至不是必需的 -您可以xargs使用-d选项来告诉它使用换行符作为分隔符:例如xargs -d'\n' -r(该-r选项是为了确保xargs在没有输入行时不执行任何操作)

其次,您使用正则表达式来解析 json 数据。这是不可靠的,极其困难甚至不可能处理复杂/嵌套的结构,脆弱,并且容易突然崩溃,原因与使用正则表达式解析 XML 或 HTML 不可靠,极其困难,并且脆弱的。不解析 XML 或 HTML用正则表达式。它不起作用这同样适用于 json

相反,您应该使用类似jq或 的东西jsonpipe从 json 数据中提取字段。例如:

jq -c 'select(.twNotif.action == "add")' file.txt | 
  xargs -d'\n' -r -L 1 -P 5 ./testscript.sh

如果你只想通过管道传输动作字段的值xargs(不带双引号),你可以这样做:

jq 'select(.twNotif.action == "add") | .twNotif.action' file.txt | 
  sed -e 's/"//g' | 
  xargs -d'\n' -r -L 1 -P 5 ./testscript.sh

对于这样的工作使用jsonpipeand可能会更容易:awk

jsonpipe < file.txt | 
  awk '$1 == "/twNotif/action" {gsub(/"/,""); print $2}' |
  xargs -d'\n' -r -L 1 -P 5 ./testscript.sh

(注意那里的重定向,与 不同jqjsonpipe仅适用于 stdin)。

BTW,jsonpipe将 json 数据转换为面向行的格式,适合与 、 、 等面向行的工具一起使用sedgrep例如awk

$ jsonpipe < file.txt
/   {}
/twNotif    {}
/twNotif/originator "api"
/twNotif/chain  "test"
/twNotif/txId   "08640-0050568a5514"
/twNotif/version    "1.0"
/twNotif/msgType    "api"
/twNotif/twData {}
/twNotif/twData/api {}
/twNotif/twData/api/hostId  "007bdcc5"
/twNotif/twData/api/user    "test"
/twNotif/twData/api/cmdTxt  "100599"
/twNotif/action "add"
/twNotif/store  "test"
/twNotif/msgTime    1467280648.971042

使用比 更容易jq,特别是如果您不需要 json 格式的输出。

它对于以可轻松与jq.例如:

$ jsonpipe <file.txt | awk '{gsub(/\//,"."); print $1}'
.
.twNotif
.twNotif.originator
.twNotif.chain
.twNotif.txId
.twNotif.version
.twNotif.msgType
.twNotif.twData
.twNotif.twData.api
.twNotif.twData.api.hostId
.twNotif.twData.api.user
.twNotif.twData.api.cmdTxt
.twNotif.action
.twNotif.store
.twNotif.msgTime

答案3

您可能看到的问题是“管道缓冲”;管道的各个组件的输出(在您的情况下grep)不再是行缓冲而是块缓冲,并且缓冲区可能是 4K 左右下一个组件不会立即看到数据。它将要当有足够的数据出现时看到它......

解决办法有点痛苦。幸运的是,该expect软件包附带了一个unbuffer可以提供帮助的命令。 man unbuffer有关如何使用它的更多详细信息。

它的工作原理是使管道的组件认为它们正在与终端通信,从而保持线路缓冲。

相关内容