martin clayton's helpful answer provides a good explanation of the problem[1], but his solution - as he states - has a potentially unwanted side effect.
Here are side-effect-free solutions:
Caveat: Solving the -i
syntax problem alone, as below, may not be enough, because there are many other differences between GNU sed
and BSD/macOS sed
(for a comprehensive discussion, see this answer of mine).
Workaround with -i
: Create a backup file temporarily, then clean it up:
With a non-empty suffix (backup-file filename extension) option-argument (a value that is not the empty string), you can use -i
in a way that works with both BSD/macOS sed
and GNU sed
, by directly appending the suffix to the -i
option.
This can be utilized to create a backup file temporarily that you can clean up right away:
sed -i.bak 's/foo/bar/' file && rm file.bak
Obviously, if you do want to keep the backup, simply omit the && rm file.bak
part.
Workaround that is POSIX-compliant, using a temporary file and mv
:
If only a single file is to be edited in-place, the -i
option can be bypassed to avoid the incompatibility.
If you restrict your sed
script and other options to POSIX-compliant features, the following is a fully portable solution (note that -i
is not POSIX-compliant).
sed 's/foo/bar' file > /tmp/file.$$ && mv /tmp/file.$$ file
This command simply writes the modifications to a temporary file and, if the sed
command succeeds (&&
), replaces the original file with the temporary one.
- If you do want to keep the original file as a backup, add another
mv
command that renames the original first.
Caveat: Fundamentally, this is what -i
does too, except that it tries to preserve permissions and extended attributes (macOS) of the original file; however, if the original file is a symlink, both this solution and -i
will replace the symlink with a regular file.
See the bottom half of this answer of mine for details on how -i
works.
[1] For a more in-depth explanation, see this answer of mine.