Portable sed -i across MacOS and Linux

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.

5 thoughts on “Portable sed -i across MacOS and Linux

  1. 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.

  2. 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.

  3. 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!)

Comments are closed.