我想监视一个文件。因此,我使用以下命令跟踪了一个文件,以每行执行一个脚本。
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
等)时,它的工作效果并不像下面所示的那样。grep
xargs
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
对于这样的工作使用jsonpipe
and可能会更容易:awk
jsonpipe < file.txt |
awk '$1 == "/twNotif/action" {gsub(/"/,""); print $2}' |
xargs -d'\n' -r -L 1 -P 5 ./testscript.sh
(注意那里的重定向,与 不同jq
,jsonpipe
仅适用于 stdin)。
BTW,jsonpipe
将 json 数据转换为面向行的格式,适合与 、 、 等面向行的工具一起使用sed
。grep
例如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
有关如何使用它的更多详细信息。
它的工作原理是使管道的组件认为它们正在与终端通信,从而保持线路缓冲。