用stdbuf解决tail -f多级管道下没有输出的问题

本文介绍如何解决Shell脚本中多级管道没有输出的问题,详细分析了缓冲区的影响及使用stdbuf和sed命令进行配置的方法。

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

问题

提取json字符串中body字段的内容,并将body字段的内容格式化后以json格式输出。

  • 日志内容是一个json字符串,如下:

COMMAND:

tail -f access-2020-10-19.log

{"method": "POST", "uri": "/api/push", "url_path": "/api/push", "status": 200, "remote_ip": "::1", "request_time": 9.083747863769531, "headers": {"Host": "localhost:8888", "User-Agent": "Go-http-client/1.1", "Content-Length": "142", "Content-Type": "application/json", "X-Push-From": "ecsagent", "Accept-Encoding": "gzip"}, "body": "b'[{\"endpoint\":\"f2e-mongo-db2\",\"metric\":\"cpu\",\"timestamp\":1603079460,\"step\":60,\"value\":10,\"counterType\":\"gauge\",\"tags\":\"team=tech\",\"source\":\"\"}]'", "time": "2020-10-19T12:13:57.756813", "response": {"body": "{\"msg\":\"ok\"}\n", "headers": [["Content-Type", "application/json; charset=utf-8"], ["Access-Control-Allow-Origin", "*"]], "status_code": 200}}

  • 提取body字段的字符串

COMMAND:

jq “.body” -r

b'[{"endpoint":"f2e-mongo-db2","metric":"cpu","timestamp":1603079460,"step":60,"value":10,"counterType":"gauge","tags":"team=tech","source":""}]'
  • 去掉开头的两个字符b’和结尾的单引号’,得到json的字符串

COMMAND:

sed “s/^b’(.*)’$/\1/”

[{"endpoint":"f2e-mongo-db2","metric":"cpu","timestamp":1603079460,"step":60,"value":10,"counterType":"gauge","tags":"team=tech","source":""}]
  • 将json字符串输出

COMMAND:

jq

[
  {
    "endpoint": "f2e-mongo-db2",
    "metric": "cpu",
    "timestamp": 1603079460,
    "step": 60,
    "value": 10,
    "counterType": "gauge",
    "tags": "team=tech",
    "source": ""
  }
]

但是将这些命令组合起来,却得不到输出:

tail -f access-2020-10-19.log | jq ".body"  -r  | sed "s/^b'\(.*\)'$/\1/"  | jq  

正确的用法为:

tail -f access-2020-10-19.log |  stdbuf -o0 jq ".body"  -r  |  sed -l "s/^b'\(.*\)'$/\1/"  | jq  

分析问题

缓冲区

IO缓冲有三种,分别是

  • 无缓冲,面向字节的设备.
  • 行缓冲,遇到换行符就输出,一般用于终端设备。
  • 全缓冲,buffer满了才输出,一般用于块设备。

标准输入流的缓冲:

  • stdin,有缓冲,行缓冲不影响stdin
  • stdout,有缓冲,在终端下是行缓冲,
  • stderr,无缓冲。

缓冲区的大小受缓冲模式影响,默认的大小为内存页的大小,通常是4k。如果stdin/stdout连接到终端设备,默认大小为1k。

stdbuf

stdbuf用于修改标准流的缓冲模式和大小

stdbuf - Run COMMAND, with modified buffering operations for its standard streams.

-i, --input=MODE
adjust standard input stream buffering

-o, --output=MODE
adjust standard output stream buffering

-e, --error=MODE
adjust standard error stream buffering

需要注意的是,如果 COMMAND 调整了标准流的缓冲(例如 tee ),那么就会覆盖stdbuf的更改,也就是不受stdbuf的影响。

原因

在标准流中,由于缓冲区的存在,所以到等到缓冲区满了才会输出下一级管道。可以将stdout的缓冲区大小都设置为0,让数据及时输出到下一级管道。

stdbuf -i0 -o0

但由于有些 COMMAND 会对标准流的缓冲做调整,这种情况下stdbuf的设置是无效的,所以要通过COMMAND的参数来指定缓冲模式。指定sed的缓冲模式为行缓冲。

 sed -l

Note: sed 在MacOSX下使用-l,linux下使用-u

最后正确命令应该是:

tail -f access-2020-10-19.log |  stdbuf -o0 jq ".body"  -r  |  sed -l "s/^b'\(.*\)'$/\1/"  | jq  

对于shell中多级管道没有输出的问题,可能的原因是缓冲区的存在,并且COMMAND的缓冲设置优先于stdbuf设置的缓冲模式,所以还要注意COMMAND的缓冲设置。

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值