Search code examples
perlshelldiskspace

How to rewrite a file from a shell script without any danger of truncating the file if out of disk space?


How to rewrite a file from a shell script without any danger of truncating the file if out of disk space?

This handy perl one liner replaces all occurrences of "foo" with "bar" in a file called test.txt:

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

This is very useful, but ...

If the file system where test.txt resides has run out of disk space, test.txt will be truncated to a zero-byte file.

Is there a simple, race-condition-free way to avoid this truncation occuring?

I would like the test.txt file to remain unchanged and the command to return an error if the file system is out of space.

Ideally the solution should be easily used from a shell script without requiring additional software to be installed (beyond "standard" UNIX tools like sed and perl).

Thanks!


Solution

  • From perldoc perlrun:

    -i[extension]

    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. The extension, if supplied, is used to modify the name of the old file to make a backup copy, following these rules:

    If no extension is supplied, no backup is made and the current file is overwritten.

    […]

    Rephrased:

    1. The backup filename is determined from the value of the -i-switch, if one is given.
    2. The original file is renamed to the new filename, and opened for the script. Renaming is atomic on most filesystems.
    3. A file with the name of the original file is opened for writing. The file will start with length zero, but is not identical to the original file (which has a different name now).
    4. After the script has finished, and if no explicit backup extension was provided, the backup file is deleted. The original file is then lost.

    Should the system run out of drive space, then the new file is endangered, not the original file which was never copied or moved (at least on filesystems with an inode-like concept).