有人可能会认为
echo foo >a
cat a | rev >a
将留下a
包含oof
;但它却留空了。
- 为什么?
- 否则如何适用
rev
于a
?
答案1
有一个应用程序可以做到这一点!sponge
from 命令正是moreutils
为此而设计的。如果您运行的是 Linux,则它可能已经安装,如果没有,请在操作系统的存储库中搜索sponge
或moreutils
。然后,你可以这样做:
echo foo >a
cat a | rev | sponge a
或者,避免乌鲁克:
rev a | sponge a
出现此行为的原因取决于命令的运行顺序。实际上是> a
执行的第一件事并> file
清空文件。例如:
$ echo "foo" > file
$ cat file
foo
$ > file
$ cat file
$
因此,当您运行时,cat a | rev >a
实际发生的情况是> a
首先运行 ,清空文件,因此当cat a
执行时文件已经是空的。这正是为什么sponge
这样写(来自man sponge
,强调我的):
Sponge 读取标准输入并将其写入指定文件。 与 shell 重定向不同,sponge 在写入输出文件之前吸收所有输入。这允许构建读取和写入同一文件的管道。
答案2
- 输出截断很早就完成了,因此 cat 看到一个空文件。
- 要么将第一个文件构造为临时文件,要么将 rev 的输出定向到您随后重命名的临时文件。
答案3
解决这个问题的另一种方法是使用不截断的写入方法
rev a | dd conv=notrunc of=a
这只有效,因为:
rev 在生成输出之前读取内容,并且输出永远不会长于已读取的数量
新文件内容与原始文件内容相同或更大(在本例中大小相同)
- dd 打开要写入的文件而不截断它。
此方法对于就地修改太大而无法保留临时副本的文件可能很有用。
答案4
>file
创建新的空文件或截断现有文件。因此,文件中没有任何内容可供rev
读取。
正如其他答案所提到的,您可以用于sponge
此目的。但sponge
并不是所有人都可以使用。
以下是仅涉及 shell 的通用解决方案:
exec 3<file; rm file; rev <&3 >file; exec 3<&-
这将打开文件(如 fd 3)并将其删除。更准确地说,这只会删除文件的目录条目,而不是文件本身。在删除该文件的所有硬链接并关闭该文件的所有句柄之前,该文件不会被删除。
接下来,rev
从“已删除的文件”中运行读取。其输出被发送到一个新文件。虽然这个新文件与原始文件同名,但它是一个不同的文件。因此,不存在冲突。
最后,我们关闭原始文件的描述符,使其被释放。
上述方法的问题是,如果出现问题,数据就会丢失。这就是为什么人们可能更喜欢以下内容(它使用的磁盘空间不比上面的多):
( rev file >file.new && mv file.new file ) || rm file.new