我有 (两个) 命令管道,需要处理来自终端的输入。使用一个命令,这很容易:
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
笔记stdbuf
并unbuffer
以不同的方式工作。 我更喜欢stdbuf
。