4

如果磁盘空间不足,如何从 shell 脚本重写文件而不会有截断文件的危险?

这个方便的 perl one liner 在名为 test.txt 的文件中将所有出现的“foo”替换为“bar”:

perl -pi -e 's/foo/bar/g' test.txt

这非常有用,但是...

如果 test.txt 所在的文件系统磁盘空间不足,test.txt 将被截断为一个零字节文件。

有没有一种简单的、无竞争条件的方法来避免这种截断的发生?

我希望 test.txt 文件保持不变,如果文件系统空间不足,命令返回错误。

理想情况下,该解决方案应该可以通过 shell 脚本轻松使用,而无需安装额外的软件(除了“标准”UNIX 工具,如 sed 和 perl)。

谢谢!

4

2 回答 2

4

一般来说,这是做不到的。请记住,空间不足的情况可能会在出现就地编辑的操作序列中的任何位置出现。一旦文件系统已满,perl 可能无法撤消先前的操作以恢复原始状态。

使用-i开关的更安全的方法是使用非空备份后缀,例如

perl -pi.bak -e 's/foo/bar/g' test.txt

这样,如果在此过程中出现问题,您仍然拥有原始数据。

如果您想自己滚动,请务必检查close系统调用返回的值。正如 Linux 手册页所述,

不检查返回值close()是一个常见但严重的编程错误。上一次write(2)操作的错误很有可能首先在 final 中报告close()。关闭文件时不检查返回值可能会导致数据无声丢失。这在 NFS 和磁盘配额中尤其明显。

就像生活中的其他事情一样,给自己留出更多的错误余地。磁盘便宜。从沙发垫上掏出零钱,然后去给自己买半 TB 左右的内存。

于 2013-04-22T10:12:49.383 回答
3

来自perldoc perlrun

-i[extension]

指定由“ <>”构造处理的文件将被就地编辑。它通过重命名输入文件、按原始名称打开输出文件并选择该输出文件作为print()语句的默认文件来实现此目的。扩展名(如果提供)用于修改旧文件的名称以制作备份副本,遵循以下规则:

如果未提供扩展名,则不进行备份并覆盖当前文件。

[…]

改写:

  1. 备份文件名由-i-switch 的值(如果给定)确定。
  2. 原始文件被重命名为新文件名,并为脚本打开。重命名在大多数文件系统上是原子的。
  3. 打开具有原始文件名称的文件进行写入。该文件将从长度零开始,但与原始文件不同(现在具有不同的名称)。
  4. 脚本完成后,如果未提供显式备份扩展名,则删除备份文件。原始文件随即丢失。

如果系统用完驱动器空间,那么新文件将受到威胁,而不是从未被复制或移动的原始文件(至少在具有类似 inode 概念的文件系统上)。

于 2013-04-22T10:11:19.380 回答