The -i
flag to ask sed
to edit a file in place works differently on Linux and MacOS. If you want to create a backup of your file before you edit it, say with the extension .bak
, then on Linux you would run
sed -i.bak myfile
but for the version of sed
that ships with MacOS you would write
sed -i '.bak' myfile
Note that this changes how sed
interprets its arguments, whether you want a backup file or not. You must specify a backup file extension, but could specify the extension as ''
, which effectively means don’t make a backup.
The difference between how the two versions of sed
handle their arguments is a minor nuisance when working interactively at the command line, but a script calling sed -i
will not work the same on Mac and Linux.
I put the line
alias sed=gsed
in my .zshrc
file so that when I type sed
the shell will actually run the Gnu version of sed
, which handles -i
as I expect.
But this does not work in bash
scripts. I tried putting the alias in my .bashrc
and .bash_profile
files, but that doesn’t work. In scripts bash
ignores aliases, no matter what config file you put them in. Here’s the relevant line from the bash man page:
Aliases are not expanded when the shell is not interactive, unless the expand_aliases shell option is set using shopt.
So the solution is to put the magic incantation
shopt -s expand_aliases
at the top of your script.
Here’s how I wrote a script using sed -i
works the same way on MacOS and Linux.
#!/usr/bin/env bash shopt -s expand_aliases if [[ "$OSTYPE" == "darwin"* ]]; then alias sed=gsed fi sed -i ...
This may seem a little heavy-handed, changing the program that I’m using just to fix a problem with the arguments. I could, for example, have the script insert ''
after -i
when running on MacOS.
But I’m not changing the program I use. Quite the opposite. The code above saves me from having to use a different program. It insures that I’m running the Gnu version of sed
on both platforms. There are other differences between the two versions of sed
. I don’t know what they are off hand, but they could lead to frustrating bugs in the future, and the code above fixes these bugs before they occur.
I prefer the following:
if [[ “$OSTYPE” == “darwin”* ]]; then
SED=gsed
else
SED=sed
fi
$SED -i …
Blindly expanding aliases might bite you at some point.
I always see if ed (the original file editor from which the stream editor sed developed) can be used instead of sed -i. The in-place option is a non-standard extension to sed.
In many cases, you can use a function instead of an alias:
sed () { gsed “$@” ; }
curious, but haven’t checked for myself: does ‘shellcheck’ catch this ambiguity?
This would probably be a good idea even without the motivating example. Apple notoriously treats the desktop userland as abandonware and most of it has been bitrotting ever since Apple forked it all for good from BSD back in like, 2005.
(In general, the Apple desktop seems to be in a bad way. People complain about it becoming more and more half-baked and buggy. Not that the fundamentals were ever all that great. A few months ago, I ran into a font kerned so badly that it looked like it had typos – it was an obscure little font called Gill Sans that some small company called Apple hadn’t updated in 22 years. Say what you will about Linux font rendering, but at least fonts can get updated there!)