使用uniq过滤管道中的相邻行

使用uniq过滤管道中的相邻行

我正在尝试使用以下命令监视主题更改:

dbus-monitor --session "interface='org.freedesktop.portal.Settings', member=SettingChanged" | grep -o "uint32 ."

现在的输出看起来像这样:

uint32 0
uint32 0
uint32 1
uint32 1
uint32 0
uint32 0
uint32 1
uint32 1

此输出来自主题切换。主题通知出于某种原因出现了两次。现在我想将其通过管道传输,uniq这样我只剩下一个条目,如下所示:

uint32 0
uint32 1
uint32 0
uint32 1

然而,在最后追加uniq不会再产生任何输出。

dbus-monitor --session "interface='org.freedesktop.portal.Settings', member=SettingChanged" | grep -o "uint32 ." | uniq

man uniq

从 INPUT(或标准输入)中过滤相邻的匹配行,写入 OUTPUT(或标准输出)。

uniq需要缓冲至少最后一个输出行才能检测相邻行,我看不出有任何原因它不能缓冲它并沿着管道传递它。我已经尝试按照建议调整行缓冲这里但结果对我来说仍然是一样的。

dbus-monitor --session "interface='org.freedesktop.portal.Settings', member=SettingChanged" | grep -o "uint32 ." | stdbuf -oL -i0 uniq

答案1

这是许多工具的常见行为,继承自带有函数操作的默认标准 C 库行为输入/输出流fopen(3)fwrite(3)...)。这已记录在setvbuf(3)(或已弃用的变体)中。几个 *nixes 都说同样的事情:*SD,索拉里斯,GNU/Linux..., 但POSIX没有具体说明,ISO/IEC 9899 太难以捉摸。GNU 告诉:

新打开的流通常是完全缓冲的,但有一个例外:连接到交互式设备(例如终端)的流最初是行缓冲的。

(还,标准错误在所有实现中通常都是无缓冲的。对于这个问答来说并不重要。)

因此,对于大多数文本过滤命令(并非全部,例如cat没有此行为),只要它不是管道中的最后一个命令,其输出就不再进行行缓冲,并且换行不会触发立即发送数据下一个要处理的命令。


这里相关命令不是uniq但是grep因为它的输出从终端切换到非终端。 GNUgrep有一个选项可以改变这种行为:--line-buffered:

--line-buffered

在输出上使用行缓冲。这可能会导致性能损失。

如果该命令没有特定的选项来选择行为,人们仍然可以使用该命令stdbufsetvbuf(3)它通过使用来改变行为LD_PRELOAD机制。要恢复不在管道末尾的命令的行缓冲行为,可以为该命令添加前缀stdbuf -oL.

对于OP的情况:

dbus-monitor --session "interface='org.freedesktop.portal.Settings', member=SettingChanged" |
    grep --line-buffered -o "uint32 ." | uniq

或者如果此grep命令也没有特定选项:

dbus-monitor --session "interface='org.freedesktop.portal.Settings', member=SettingChanged" |
    stdbuf -oL grep -o "uint32 ." | uniq

请注意,在这两种情况下,uniq处于管道末端不需要对其默认行为进行任何调整。

如果稍后管道被扩充并且uniq不再位于末端,从而不再输出到终端,它也会改变行为并且需要相同的处理。例如,命令uniq在终端上单独的行缓冲行为:

uniq

更改为全缓冲:

uniq | cat

但可以通过以下方式反转为行缓冲:

stdbuf -oL uniq | cat

相关内容