如何从shell脚本重写文件,而不会因为磁盘空间不足而导致文件被截断



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

这个方便的perl一行代码将test.txt文件中出现的所有"foo"替换为"bar":

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

这是非常有用的,但是…

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

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

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

理想情况下,解决方案应该可以很容易地从shell脚本中使用,而不需要安装额外的软件(除了"标准"UNIX工具,如sed和perl)。

谢谢!

一般来说,这是不可能做到的。请记住,空间外条件可以出现在具有外观的的操作序列中的任何位置。一旦文件系统被填满,perl可能无法撤销以前的操作以恢复原始状态。

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

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

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

如果您想滚动您自己的,一定要检查从close系统调用返回的值。正如Linux手册页所指出的,

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

和生活中的其他事情一样,给自己留下更多犯错的余地。磁盘很便宜。从你的沙发靠垫里拿出零钱,去给自己买半个太字节的空间。

From perldoc perlrun:

-i[extension]

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

如果没有提供扩展名,则不进行备份,当前文件被删除重写。

[…]

描述:

  1. 备份文件名由-i -开关的值决定,如果有的话。
  2. 将原始文件重命名为新文件名,并为脚本打开。在大多数文件系统上,重命名是原子性的。
  3. 打开与原文件名相同的文件进行写入操作。该文件将以长度为0开始,但与原始文件不相同(现在有不同的名称)。
  4. 脚本完成后,如果没有提供明确的备份扩展名,则删除备份文件。原始文件将丢失。

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

最新更新