无法重定向剪切输出

bot*_*t47 4 bash io-redirection cut netcat

当我尝试重定向cut它的输出时,它似乎总是为空。如果不重定向它,输出将按预期显示在终端中。这适用于 OS X 10.10 和 Linux 4.1.6。

这有效:

root@karla:~# nc 10.0.2.56 30003
[...] lots of lines [...]
Run Code Online (Sandbox Code Playgroud)

这有效:

root@karla:~# nc 10.0.2.56 30003 | cat
[...] lots of lines [...]
Run Code Online (Sandbox Code Playgroud)

这有效:

root@karla:~# nc 10.0.2.56 30003 | cut -d, -f 15,16
[...] lots of lines [...]
Run Code Online (Sandbox Code Playgroud)

这不

root@karla:~# nc 10.0.2.56 30003 | cut -d, -f 15,16 | cat
[nothing]
Run Code Online (Sandbox Code Playgroud)

这再次DOES

root@karla:~# cat messung1 | cut -d, -f15,16 | cat
[...] lots of lines [...]
Run Code Online (Sandbox Code Playgroud)

这不仅限于cat之后cutgrep,tee和标准重定向使用>也不起作用。

有什么问题吗?

Sté*_*las 11

没有输出并不是因为它是成块的。

像许多程序一样,当它的输出不再是终端时,cut缓冲它的输出。也就是说,它仅在积累了一个缓冲区时才写入数据。通常,虽然 YMMV 是 4 或 8 KiB。

您可以通过比较轻松地验证它:

(echo foo; sleep 1; echo bar) | cut -c2-
Run Code Online (Sandbox Code Playgroud)

和:

(echo foo; sleep 1; echo bar) | cut -c2- | cat
Run Code Online (Sandbox Code Playgroud)

在第一种情况下,cut输出oo\n然后ar\n一秒后,而在第二种情况下,oo\nar\n在 1 秒后切断输出,即当它看到其输入结束并在退出时刷新其输出。

在您的情况下,由于 stdin is nc,它只会在连接关闭时看到其输入的结尾,因此它只会在积累了 4KiB 的要写入的数据后才开始输出任何内容。

为了解决这个问题,有几种方法是可能的。

  • 在 GNU 或 FreeBSD 系统上,您可以使用stdbuf可以调整某些命令的缓冲行为的实用程序(它不适用于所有命令,因为它使用 LD_PRELOAD hack 来预配置 stdio 缓冲行为)。

    ... | stdbuf -oL cut -d, -f15,16 | cat
    
    Run Code Online (Sandbox Code Playgroud)

    会告诉cut在其标准输出上进行基于行的缓冲。

  • 一些像 GNUgrep这样的命令有影响它们缓冲的选项。(--line-buffered在 GNU 的情况下grep)。

  • 您可以使用伪 tty 包装器强制命令的标准输出成为终端。然而,这些解决方案中的大多数都有一些缺点和局限性。unbuffer expect例如,经常提到的解决此类问题的脚本有许多错误。

    一个不太糟糕的是使用时socat

    ... | socat -u 'exec:"cut -d, -f15,16",pty,raw' -
    
    Run Code Online (Sandbox Code Playgroud)
  • 您可以将文本实用程序替换为支持无缓冲输出的更高级别的文本处理工具。

    • awk例如,GNU具有fflush()刷新其输出的功能。所以你cut -d, -f15,16可以写成:

      awk -F, -vOFS=, '{print $15,$16;fflush()}'
      
      Run Code Online (Sandbox Code Playgroud)
    • 如果您awk缺少该fflush()功能,则可以system("")改用。这通常是执行命令的命令。在这里,我们将执行一个空命令,但实际上使用它是为了awk在运行命令之前刷新其标准输出。

    • 或者你可以使用perl

      perl -F, -lane 'BEGIN{$,=",";$|=1} print @F[14..15]'
      
      Run Code Online (Sandbox Code Playgroud)