Andrew Marshall
Andrew Marshall

Reputation: 96994

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.

Upvotes: 12

Views: 870

Answers (3)

mob
mob

Reputation: 118635

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
    

Upvotes: 7

choroba
choroba

Reputation: 241968

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.

Upvotes: 6

Quentin
Quentin

Reputation: 943981

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.

Upvotes: 3

Related Questions