为什么这个简单的命令在使用 emacs shell (eshell) 时失败?
cat file.txt | wc
我有一个包含 10241 行的文件。每行少于 50 个字符。在我启动此命令的情况下,大约 90% 的情况下,它会给出错误的结果,即行计数。尽管如此,没有给出错误消息。
看起来管道破裂是一个很常见的话题,但我还没有找到任何合理的解释。此外,没有人提出任何解决方法。我怎样才能让这个简单的命令可靠地工作?
当然,我也可以直接逃跑wc file.txt
。但我正在寻找一种更通用的解决方案,其中任何工具都可以正常工作 Piped cat: cat file.txt | any_tool_here
。
细节
我使用的是CentOS 5。使用时出现此问题eshell(emacs shell)。我正在使用 GNU Emacs 24.5.2。
实验
结果示例cat file.txt | wc
(预期:第一列始终为 10241)。
- 8568 25706 110571
- 9837 29513 126947
- 5395 16187 69615
- 9202 27608 118757
- 7299 21899 94199
- 9837 29513 126947
结果示例使用wc file.txt
:
- 10241 30723 132156
- 10241 30723 132156
- 10241 30723 132156
- 10241 30723 132156
- 10241 30723 132156
- 10241 30723 132156
cat 命令本身(单独执行时)工作正常。我使用以下命令验证了它(几次)cat file.txt > file2.txt
:。然后,我比较了这两个文件,它们是相同的。
答案1
从有关所使用的 shell 的信息来看 ( eshell
),该 shell 的流处理方面似乎是罪魁祸首。通常,管道意味着打开管道的两端+ fork/exec,因此您会得到两个共享管道文件描述符的进程,并且通信直接通过内核进行。这样,就不会丢失任何东西 - 它保证是安全的(尽管如果管道或任何涉及的流被缓冲,您可能必须等待第一个进程正常退出才能刷新流的最后一个块)。
从摘录来看电子外壳手册:
Eshell 不能替代 bash 或 zsh 等系统 shell。当您想要在 Emacs 和外部进程之间移动文本时,请使用 Eshell;如果您只想将输出从一个外部进程通过管道传输到另一个(然后是另一个,依此类推),请使用系统 shell,因为 Emacs 的 IO 系统是面向缓冲区的,而不是面向流的,并且在此类任务中效率非常低。如果您想在 Eshell 中编写 shell 脚本,请不要这样做;要么编写 elisp 库,要么使用系统 shell。
eshell 没有以正常方式执行此操作,而是使用其“缓冲区”(emacs 的打开文件表示)作为数据的中间存储来伪造管道,并且(无需进一步研究)我猜想在某些时候wc
会执行read
,并emacs
以空块响应(返回 0read
表示流已结束),而不是等待第一个程序的更多输入来填充缓冲区。如果是这样的话,就意味着 eshell 在处理管道时不仅效率低下而且还存在缺陷。