【问题标题】:Weird behavior when prepending to a file with cat and tee使用 cat 和 tee 附加到文件时的奇怪行为
【发布时间】:2014-10-09 16:36:32
【问题描述】:

prepend to a file one liner shell? 解决问题的一个方法是:

cat header main | tee main > /dev/null

正如一些 cmets 所注意到的,这不适用于大文件。

这是一个有效的例子:

$ echo '1' > h
$ echo '2' > t
$ cat h t | tee t > /dev/null
$ cat t
1
2

以及它在哪里中断:

$ head -1000 /dev/urandom > h
$ head -1000 /dev/urandom > t
$ cat h t | tee t > /dev/null
^C

命令挂起,杀死它后我们剩下:

$ wc -l t
7470174 t

是什么导致上述命令卡住并无限添加行的行为? 1 行文件场景有什么不同?

【问题讨论】:

    标签: linux bash shell unix


    【解决方案1】:

    行为是完全不确定的。当您执行cat header main | tee main > /dev/null 时,会发生以下情况:

    1) cat opens header
    2) cat opens main
    3) cat reads header and writes its content to stdout
    4) cat reads main and writes its content to stdout
    5) tee opens main for writing, truncating it
    6) tee reads stdin and writes the data read into main
    

    上面的排序是一种可能的排序,但是这些事件可能以许多不同的顺序发生。 5 必须在 6 之前,2 必须在 4 之前,1 必须在 3 之前,但是完全可以排序为 5,1,3,2,4,6。在任何情况下,如果文件很大,很可能会在步骤 4 完成之前执行步骤 5,这将导致部分数据被丢弃。完全有可能第 5 步首先发生,在这种情况下,之前 main 中的所有数据都将丢失。

    您看到的特殊情况很可能是 cat 阻塞写入并在完成读取输入之前进入睡眠状态的结果。 tee 然后将更多数据写入t 并尝试从管道读取,然后进入睡眠状态,直到 cat 写入更多数据。 cat 写入一个缓冲区,tee 将其放入 t,然后循环重复,cat 重新读取 tee 正在写入 t 的数据。

    【讨论】:

      【解决方案2】:

      猫头主要| tee main > /dev/null

      这是一个可怕的想法。你永远不应该有一个既可以读取文件又可以写入文件的管道。

      您可以先将结果放在一个临时文件中,然后再将其移动到位:

      cat header main >main.new && mv main{.new,}
      

      或者为了最大限度地减少文件的两个副本存在并且永远不会同时在目录中可见的时间量,您可以在打开原始文件以直接读取和写入新文件后删除它它以前的位置。但是,这确实意味着文件根本不存在的短暂间隙。

      exec 3<main && rm main && cat header - <&3 >main && exec 3<&-
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-05-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-01-08
        • 1970-01-01
        • 2017-02-17
        相关资源
        最近更新 更多