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

Perl 如果磁盘空间不足,如何从shell脚本重写文件而不存在截断文件的危险?,perl,shell,diskspace,Perl,Shell,Diskspace,如果磁盘空间不足,如何从shell脚本重写文件而不存在截断文件的危险 这个方便的perl one liner将test.txt文件中出现的所有“foo”替换为“bar”: perl -pi -e 's/foo/bar/g' test.txt 这是非常有用的,但是 如果test.txt所在的文件系统磁盘空间不足,test.txt将被截断为零字节文件 有没有一种简单的、无竞争条件的方法来避免这种截断的发生 我希望test.txt文件保持不变,如果文件系统空间不足,命令将返回错误 理想情况下,该解决

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

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

perl -pi -e 's/foo/bar/g' test.txt
这是非常有用的,但是

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

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

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

理想情况下,该解决方案应该可以从shell脚本轻松使用,而无需安装其他软件(除了sed和perl等“标准”UNIX工具之外)

谢谢

来自:

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

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

[……]

重新措辞:

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

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

    一般来说,这是无法做到的。请记住,空间不足条件可能会影响到动作序列中的任何位置,从而产生就地编辑的效果。一旦文件系统已满,perl可能无法撤消以前的操作以恢复原始状态

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

    perl-pi.bak-e's/foo/bar/g'test.txt 这样,如果在过程中出现问题,您仍然拥有原始数据

    如果您想自己滚动,请确保检查
    close
    系统调用返回的值。正如Linux手册页面所指出的

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


    和生活中的其他事情一样,给自己留有更多的犯错余地。磁盘很便宜。从沙发垫上掏出零钱,给自己买半TB左右。

    这真的会发生吗?据我所知,就地编辑
    -I
    开关的作用是首先写入临时文件,一旦写入,将该文件复制到原始文件上。这意味着,如果临时文件写入失败,该文件根本不会被覆盖。一个警告是,文件系统实际上并没有满——我只是达到了磁盘配额(通过/aquota.user强制执行)。是的,使用perl-pi.bak-e's/foo/bar/g'test.txt确实会将我的原始文件保留在test.txt.bak中,即使磁盘空间耗尽。谢谢@Kieran注意到,运行这样一个一行程序两次将覆盖原来的代码。 perl -pi.bak -e 's/foo/bar/g' test.txt