Is there a way to make perl -i not clobber symlinks?

I wonder whether the small sponge general-purpose utility ("soak up standard input and write to a file") from moreutils will be helpful in this case and whether it will follow the symlink.

The author describes sponge like this:

It addresses the problem of editing files in-place with Unix tools, namely that if you just redirect output to the file you're trying to edit then the redirection takes effect (clobbering the contents of the file) before the first command in the pipeline gets round to reading from the file. Switches like sed -i and perl -i work around this, but not every command you might want to use in a pipeline has such an option, and you can't use that approach with multiple-command pipelines anyway.

I normally use sponge a bit like this:

sed '...' file | grep '...' | sponge file

If you know for a fact that somefile is a symlink, you can explicitly supply the full path to file with the following:

perl -pi.bak -e 's/foo/bar/' $(readlink somefile)

That way, the original symlink stays intact since the replacement is now done directly with the original file.


If you're dealing with a single file that, the command would look as follows:

perl -i -pe'...' -- "$qfn"

The solution then is the following:

perl -i -pe'...' -- "$( readlink -e -- "$qfn" )"

This handles both symlinks and non-symlinks.


If you're dealing with an arbitrarily large number of files, the command would look as follows:

... | xargs -r perl -i -pe'...' --

The solution then is the following:

... | xargs -r readlink -ze -- | xargs -r0 perl -i -pe'...' --

This handles both symlinks and non-symlinks.


Caveat readlink is not a standard command, so it's presence, its arguments, and its functionality varies by system, so be sure to check your documentation. For example, some implementations have -f (which you could also use here), but not -e, some neither. busybox's readlink -f behaves like GNU readlink -e. And some systems have a realpath command instead of readlink which generally behaves like GNU readlink -f.

Beware that with GNU readlink -e, symlinks that can't be resolved to an existing file are silently removed.