为什么管道到同一个文件在某些​​平台上不起作用?

Alb*_*lby 5 bash pipeline overwrite in-place io-redirection

在cygwin中,以下代码工作正常

$ cat junk
bat
bat
bat

$ cat junk | sort -k1,1 |tr 'b' 'z' > junk

$ cat junk
zat
zat
zat
Run Code Online (Sandbox Code Playgroud)

但是在linux shell(GNU/Linux)中,似乎覆盖不起作用

[41] othershell: cat junk
cat
cat
cat
[42] othershell: cat junk |sort -k1,1 |tr 'c' 'z'
zat
zat
zat
[43] othershell: cat junk |sort -k1,1 |tr 'c' 'z' > junk
[44] othershell: cat junk
Run Code Online (Sandbox Code Playgroud)

两个环境都运行BASH.

我问这个是因为有时在我进行文本操作之后,由于这个警告,我被迫制作tmp文件.但我知道在Perl中,你可以在执行某些操作/操作后给出"i"标志来覆盖原始文件.我只是想问一下unix管道中是否有任何万无一失的方法来覆盖我不知道的文件.

Tod*_*obs 10

这里有四个要点:

  1. "无用的." 不要那样做.
  2. 你实际上并没有对sort进行排序.不要那样做.
  3. 你的管道没有说出你的想法.不要那样做.
  4. 你正试图在读取文件的同时覆盖它.不要那样做.

您获得不一致行为的原因之一是您正在使用具有重定向的进程,而不是重定向整个管道的输出.差异很微妙,但很重要.

您想要的是使用命令分组创建复合命令,以便您可以重定向整个管道的输入和输出.在您的情况下,这应该正常工作:

{ sort -k1,1 | tr 'c' 'z'; } < junk > sorted_junk
Run Code Online (Sandbox Code Playgroud)

请注意,无需排序,您也可以跳过sort命令.然后您的命令可以在不需要命令分组的情况下运行:

tr 'c' 'z' < junk > sorted_junk
Run Code Online (Sandbox Code Playgroud)

保持重定向和管道尽可能简单.它使调试脚本变得更加容易.

但是,如果由于某种原因仍想滥用管道,可以使用moreutils包中的sponge实用程序.手册页说:

sponge读取标准输入并将其写入指定文件.与shell重定向不同,海绵在打开输出文件之前会吸收其所有输入.这允许限制读取和写入同一文件的管道.

因此,您的原始命令行可以像这样重写:

cat junk | sort -k1,1 | tr 'c' 'z' | sponge junk
Run Code Online (Sandbox Code Playgroud)

并且由于在海绵从管道接收到EOF之前不会覆盖垃圾,您将获得您期望的结果.


lar*_*sks 6

一般来说,这可能会破裂.管道中的进程都是并行启动的,因此> junk在行的末尾通常会截断您的输入文件,然后才能在流水线的开头处完成(甚至开始)读取它.

即使Cygwin下的bash让你侥幸逃脱,你也不应该依赖它.一般的解决方案是重定向到临时文件,然后在管道完成时重命名它.