Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to ignore read-only files with `perl -i`?

Perl’s -i switch appears to modify read-only files:

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

This is unexpected, as the command should not have been able to modify the file per its permissions. Expectedly, trying to update it via other means fails:

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

Why is Perl modifying read-only files (and how?), and, most importantly: how can I get Perl to not do so?

The only somewhat informative resource I can find on this is in the Perl FAQ:

The permissions on a file say what can happen to the data in that file. … If you try to write to the file, the permissions of the file govern whether you're allowed to.

Which ultimately seems like its saying it shouldn’t be able to write to it, since the file system says you cannot.

like image 756
Andrew Marshall Avatar asked Apr 03 '13 15:04

Andrew Marshall


3 Answers

Filter @ARGV in a BEGIN block:

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

Now if none of the files on the command line are writable, @ARGV will be empty and the ARGV filehandle will try to read from STDIN. I can think of two ways to keep this from being a problem:

  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
    
like image 146
mob Avatar answered Nov 01 '22 22:11

mob


See the documentation in perlrun:

renaming the input file, opening the output file by the original name, and selecting that output file as the default for print() statements

(...)

For a discussion of issues surrounding file permissions and -i, see "Why does Perl let me delete read-only files? Why does -i clobber protected files? Isn't this a bug in Perl?" in perlfaq5.

like image 6
choroba Avatar answered Nov 02 '22 00:11

choroba


From perlrun:

-i
specifies that files processed by the <> construct are to be edited in-place. It does this by renaming the input file, opening the output file by the original name, and selecting that output file as the default for print() statements.

So it is doesn't really modify the file. It moves the file out of the way (which requires directory write permissions, not file write permissions) and then creates a new one with the old name.

how can I get Perl to not do so?

I don't think you can when you use -i.

like image 3
Quentin Avatar answered Nov 02 '22 00:11

Quentin