如何使用 `perl -i` 忽略只读文件?

12

Perl的-i开关似乎可以修改只读文件:

$ echo 'foobar' > tmp.txt
$ chmod -w tmp.txt
$ perl -pi -w -e 's/foobar/FOOBAR/' tmp.txt
$ cat tmp.txt
FOOBAR

这是意外的,因为根据其权限,该命令不应该能够修改文件。可预见地,尝试通过其他方式更新它会失败:

$ echo 'barbaz' > tmp.txt
-bash: tmp.txt: Permission denied

为什么Perl会修改只读文件(以及如何进行修改)?最重要的是:如何让Perl不这样做?

我找到的唯一有些信息的资源在Perl FAQ中:

文件权限规定了该文件中数据的使用方式。如果您尝试向文件写入内容,则文件权限将会决定是否允许这样做。

这似乎意味着它不应该能够写入它,因为文件系统说你不能这样做。


6
如果你有对于目录的写权限,那么你和Perl都有权限去(1)重命名一个只读文件并且(2)用旧的文件名创建一个新文件。这是一个好问题。 - mob
3个回答

7

BEGIN块中过滤@ARGV

perl -pi -e 'BEGIN{@ARGV=grep{-w $_}@ARGV} s/foobar/FOOBAR/' files

现在,如果命令行上的文件都无法写入,@ARGV 将为空,ARGV 文件句柄将尝试从 STDIN 读取。我可以想到两种方法来解决这个问题:

  1. Close STDIN in the BEGIN block, too

    perl -pi -e 'BEGIN{close STDIN;@ARGV=grep{-w $_}@ARGV}s/foobar/FOOBAR/' files
    
  2. Always call this one-liner redirecting input from /dev/null

    perl -pi -e 'BEGIN{@ARGV=grep{-w $_}@ARGV}s/foobar/FOOBAR/' files < /dev/null
    

6
请参阅perlrun文档:
重命名输入文件,以原始名称打开输出文件,并将该输出文件选择为print()语句的默认值。
(...)
有关文件权限和-i的问题的讨论,请参见perlfaq5中的“为什么Perl允许我删除只读文件?为什么-i会破坏受保护的文件?这不是Perl中的错误吗?”

3

来自perlrun

-i
指定由<>结构处理的文件进行原地编辑。它通过重命名输入文件,以原始名称打开输出文件,并将该输出文件选为print()语句的默认值来实现这一点。

所以它并没有真正修改文件。它将文件移到了一边(这需要目录写入权限,而不是文件写入权限),然后用旧的名称创建一个新文件。

如何让Perl不这样做?

我认为在使用-i时无法禁止此操作。


嗯,有趣。看起来它还会将旧文件的权限复制到新文件中,这也是我(错误地)排除它是用替换文件交换文件的原因。那么我想唯一的方法就是预先过滤只读文件,或者不使用-i了? - Andrew Marshall
这是我会采取的方法。 - Quentin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接