将管道弯曲回原点

Too*_*rot 24 pipe io-redirection

有人可能认为

echo foo >a
cat a | rev >a
Run Code Online (Sandbox Code Playgroud)

将离开a包含oof; 但它是空的。

  1. 为什么?
  2. 否则如何适用reva

ter*_*don 32

有一个应用程序!在sponge从命令moreutils是专为在于此。如果您正在运行 Linux,它可能已经安装,如果没有,请在您的操作系统存储库中搜索spongemoreutils。然后,你可以这样做:

echo foo >a
cat a | rev | sponge a
Run Code Online (Sandbox Code Playgroud)

或者,避免UUoC

rev a | sponge a
Run Code Online (Sandbox Code Playgroud)

这种行为的原因取决于您的命令运行的顺序。这> a实际上是执行的第一件事并> file清空文件。例如:

$ echo "foo" > file
$ cat file
foo
$ > file
$ cat file
$
Run Code Online (Sandbox Code Playgroud)

因此,当您运行时cat a | rev >a,实际发生的> a是首先运行该文件,清空该文件,因此当cat a该文件被执行时,该文件已经是空的。这正是sponge写作的原因(来自man sponge,重点是我的):

海绵读取标准输入并将其写出到指定的文件。 与 shell 重定向不同,海绵在写入输出文件之前吸收所有输入。这允许构建读取和写入同一文件的管道。

  • @ivan_pozdeev 查看源代码,海绵将输出写入临时文件,然后将其移动到您指定的文件中,因此它实际上不会在内存中存储比缓冲区大小用于写入的数据更多的数据。 (6认同)
  • @吉姆。这本身就是一个很好的问题。它有多种不同之处:您可以在两个部分之间中断该命令,并最终得到两个文件。此外,它可能会耗尽磁盘空间,因为临时文件在同一个文件系统上,而不是在“TMPDIR”中。它可以覆盖文件,因为它不使用由`tempfile` 生成的名称。并且它包含两个对同一个文件的名称引用,这允许因拼写错误而存在差异。(它需要对名称具有引用访问权限,但这里就是这种情况)。而且,最重要的是,它代表了一种抽象。 (2认同)

sto*_*ent 10

  1. 输出截断很早就完成了,所以 cat 看到一个空文件。
  2. 要么将第一个文件构造为临时文件,要么将 rev 的输出定向到一个临时文件,然后您对其进行重命名。


Jas*_*sen 8

解决此问题的另一种方法是使用不会截断的写入方法

  rev a | dd conv=notrunc of=a
Run Code Online (Sandbox Code Playgroud)

这只有效,因为:

  1. rev 在产生输出之前读取内容并且输出永远不会超过已经读取的数量

  2. 新文件内容与原始文件大小相同或更大(在这种情况下为相同大小)

  3. dd 打开要写入的文件而不截断它。

这种方法对于就地修改太大而无法保留临时副本的文件可能很有用。