Bash 为什么管道到同一个文件不';在某些平台上不工作?

Bash 为什么管道到同一个文件不';在某些平台上不工作?,bash,pipeline,overwrite,io-redirection,in-place,Bash,Pipeline,Overwrite,Io Redirection,In Place,在cygwin中,以下代码可以正常工作 $ cat junk bat bat bat $ cat junk | sort -k1,1 |tr 'b' 'z' > junk $ cat junk zat zat zat 但是在linux shell(GNU/linux)中,覆盖似乎不起作用 [41] othershell: cat junk cat cat cat [42] othershell: cat junk |sort -k1,1 |tr 'c' 'z' zat zat zat

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

$ cat junk
bat
bat
bat

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

$ cat junk
zat
zat
zat
但是在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
这两种环境都运行BASH


我这样问是因为有时在我进行文本处理之后,由于这个警告,我被迫创建tmp文件。但是我知道,在Perl中,您可以在一些操作/操作之后,使用“I”标志覆盖原始文件。我只是想问一下,在unix管道中是否有任何万无一失的方法来覆盖我不知道的文件。

一般来说,这可能会被破坏。管道中的进程都是并行启动的,因此在管道前端的进程完成(甚至开始)读取输入文件之前,行末尾的垃圾通常会截断输入文件

即使在Cygwin领导下的bash让你逍遥法外,你也不应该依赖它。一般的解决方案是重定向到临时文件,然后在管道完成后重命名它。

这里有四个要点:

  • “猫的无用用途。”不要那样做
  • 实际上,您并没有使用sort对任何内容进行排序。不要那样做
  • 你的管道没有说你认为它能做什么。不要那样做
  • 您试图在读取文件时将其重写到位。不要那样做
  • 导致行为不一致的原因之一是,您正在通过管道连接到具有重定向的进程,而不是将管道的输出作为一个整体重定向。两者之间的区别很微妙,但很重要

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

    { sort -k1,1 | tr 'c' 'z'; } < junk > sorted_junk
    

    而且,由于在海绵从管道接收EOF之前,垃圾邮件不会被覆盖,因此您将获得预期的结果。

    如果要编辑该文件,您可以使用编辑器

    ex junk << EOF
    %!(sort -k1,1 |tr 'b' 'z')
    x
    EOF
    

    ex junk覆盖管道中的同一个文件不是建议,因为当你犯了错误时,你无法将其取回(除非你有备份或它处于版本控制之下)

    发生这种情况是因为管道中的输入和输出是自动缓冲的(这给您一种它工作的印象),但实际上它是并行运行的。不同的平台可以以不同的方式(根据设置)缓冲输出,因此在某些平台上,您最终得到的是空文件(因为文件将在开始时创建),而在另一些平台上,您得到的是半成品文件

    解决方案是,当文件仅在遇到具有完全缓冲和处理输入的EOF时才被重写时,使用某种方法

    这可以通过以下方式实现:

    • 使用实用程序,它可以在打开输出文件之前吸收所有输入

      这可以通过
      spoone
      (与
      expect
      package中的
      unbuffer
      相反)来完成

    • 避免使用I/O重定向语法(这会在启动命令之前创建空文件)

      例如,使用
      tee
      (缓冲其标准流),例如:

      cat junk | sort | tee junk
      
      这只适用于
      排序
      ,因为它需要所有输入来处理排序。因此,如果您的命令没有使用
      排序
      ,请添加一个

      另一个可以使用的工具是
      stdbuf
      ,它修改其标准流的缓冲操作,您可以在其中指定缓冲区大小

    • 使用可就地编辑文件的文本处理器(如
      sed
      ex

      例如:

      $ ex -s +'%!sort -k1' -cxa myfile.txt
      $ sed -i '' s/foo/bar/g myfile.txt
      

    使用以下简单脚本,您可以使其按照您的意愿工作:

    $ cat junk | sort -k1,1 |tr 'b' 'z' | overwrite_file.sh junk
    
    覆盖_file.sh 请注意,如果您不想将更新后的文件发送到stdout,可以使用这种方法

    覆盖文件\u no\u output.sh
    Perl和sed的
    -i
    在幕后执行临时文件操作。请参阅,感谢您深思熟虑的回答!我改变了输入,只是为了把问题弄清楚。需要对原始输入进行排序。但你是对的。在本例中,sort不起任何作用
    海绵
    就像
    @nobar一样,它们是不同的。请参阅软件包中的内容。@CodeGnome:很抱歉,我不打算发布这些内容。这只是我从未完成的一句话的开头。相反,我写了一篇关于
    海绵
    的使用说明。
    
    $ ex -s +'%!sort -k1' -cxa myfile.txt
    $ sed -i '' s/foo/bar/g myfile.txt
    
    $ cat junk | sort -k1,1 |tr 'b' 'z' | overwrite_file.sh junk
    
    #!/usr/bin/env bash
    
    OUT=$(cat -)
    
    FILENAME="$*"
    
    echo "$OUT" | tee "$FILENAME"
    
    #!/usr/bin/env bash
    
    OUT=$(cat -)
    
    FILENAME="$*"
    
    echo "$OUT" > "$FILENAME"