通过管道运行来自 stdin 的输入

通过管道运行来自 stdin 的输入

我有 (两个) 命令管道,需要处理来自终端的输入。使用一个命令,这很容易:

grep "b"

从 stdin 读取,它是终端,并将包含 b 的行打印到 stdout(也是终端)。

但如果我通过管道执行另一个命令,例如:

grep "b" | tr 'a' 'a'

它仍然接受来自终端的输入,但从不输出任何东西。我能强制它做任何事情的唯一方法是使用文件启动管道,如下所示:

cat "file.txt" | grep "b" | tr 'c' 'a'

然后它的行为就像预期的那样,打印包含 b 的 file.txt 行并用 a 替换所有出现的 c。

那么,有没有办法让它读取 stdin 而不是“file.txt”,就像只有一个命令时一样?

cat - | grep "b" | tr 'c' 'a'

不工作。

答案1

可能有多种方法可以做到这一点。一种常见的方法是使用“read”从 stdin 读取输入。不幸的是,这一次只能处理一行,因此它通常封装在循环中。

另一个更棘手的解决方案可能是使用命名管道或套接字从另一个 TTY 或套接字“注入”输入。

对于某些类型的引导输入,使用“expect”代替read可能会更合适。

答案2

grep b | tr c a 从终端读取。您不会立即看到整个管道的输出,因为grep和之间有一个缓冲区tr

可能的情况:

  • 如果你写入足够多的匹配数据,那么缓冲区最终会填满,tr会得到一些输入,你会看到一些输出。这很难通过纯手动输入来实现,因为缓冲区可能相对较大
  • 如果您通过点击Ctrl+来指示 EOF D,那么tr将获得它应该获得的所有内容,并且您将看到所有预期的输出。手动测试很容易。
  • 如果您放弃并按下Ctrl+键C,那么您将终止管道并丢失缓冲区中的所有数据。我猜这就是您所做的事情,这就是为什么您认为该命令“永远不会输出任何内容”。

grep可能支持或不支持该--line-buffered选项。如果支持,则以下命令将立即打印匹配的行:

grep --line-buffered b | tr c a

您可能在使用 以外的程序时遇到过此问题grep。对于任何程序,请尝试以下常规解决方案(其中grep b仅为示例):

stdbuf -o L grep b | tr c a
unbuffer -p grep b | tr c a    # probably without echoing input

笔记stdbufunbuffer以不同的方式工作。 我更喜欢stdbuf

相关内容