Sed on Mac OS X 10.5 Leopard

Imagine you’re a PHP developer that uses OS X. You’re given 50+ php files that all have a line that needs to be changed. 50 files, one change? Hmmm sounds like maybe I could use automator to do this! Well I don’t know how to use automator ๐Ÿ˜‰ Luckily, I’m a unix geek, and even more luckily, OS X has a fairly strong unix back end and a great terminal emulator. So how does that help? Well if you’re a unix geek you know that this sort of problem just screams “SED! use SED! this is what SED IS FOR!” And it’s true. This is where sed is great.

Say you want to change this line:

 $config_file = $_SERVER['DOCUMENT_ROOT']."/dev/config.php";

To this:

 $config_file = "dev/config.php";

As a matter of fact… wait a minute! I don’t want to just change that line… no I want to be able to show what I’ve changed, so that the next person who looks at this can see what it used to say. In this instance it’s also important to show the work I’ve done because I’m making changes to someone else’s work. So what I want to do is comment out the line and then add a new line with my change underneath. Sounds a bit more complicated right? Well it’s not really – unless you’re using OS X (you’ll see why in a minute *sigh*). The goal is to end up with this:

 //$config_file = $_SERVER['DOCUMENT_ROOT']."/dev/config.php";
 $config_file = "dev/config.php";

On the linux machine I keep around the office the command to perform this change on the all php files in the current directory looks like this ( I had to split the line to fit on the site):

sed -i .bak "s/^\$config_file = \
\$_SERVER\['DOCUMENT_ROOT'\]\.\"\/dev\/config\.php\"\;/\/\/\
\$config_file = \$_SERVER\['DOCUMENT_ROOT'\]\.\"\/dev\/config\.php\"\;\
\n\$config_file = \"dev\/config\.php\"\;/g" *.php

The breakdown of the above command is as follows:

sed | the sed command

-i .bak | the -i option means do this change “in place” and copy the original file to originalname.bak

| we use the double quote here to start the sed command because there are single quotes in the command

s/^\$config_file =
\$_SERVER\[‘DOCUMENT_ROOT’\]\.\”\/dev\/config\.php\”\;/\/\/\$config_file
= \$_SERVER\[‘DOCUMENT_ROOT’\]\.\”\/dev\/config\.php\”\;\n\$config_file = \”dev\/config\.php\”\;/g
| this is the magic of sed. it looks for the line we want to change, adds // to the beginning of it, adds a new line after it, and then adds the text that we want after the new line. Amazing isn’t it?

| the command ends with the double quote

and, finally:

*.php | represents all of the files that end in .php in the current directory.

Now if you thought that was a mess, check out how that command needs to look in order to work on OS X (10.5.1, possibly other versions)

sed -i .bak "s/^\$config_file = \
\$_SERVER\['DOCUMENT_ROOT'\]\.\"\/dev\/config\.php\"\;/\/\/\
\$config_file = \$_SERVER\['DOCUMENT_ROOT'\]\.\"\/dev\/config\.php\"\;\
\\"$'\n'"\
\$config_file = \"dev\/config\.php\"\;/g" index.php

The crazy part here is the need to use \\”$’\n'”\

What that represents is an escaped version of the literal newline character. With the version of sed currently in OS X, that is the only way (that I could find) to add a newline character with sed. Now you know too.

So that concludes this edition of mac unix geekery… until next time…

Join the Conversation

8 Comments

  1. The issue here is not with OSX per-se as it is with the default version of sed you get with OSX.

    /usr/bin/sed is the BSD version from May 10, 2005

    Optionally, you can install gnu sed or gsed via ports (currently 4.2.1 from June 2009) that behaves as you are accustomed to on linux.

    $ echo “a b c d” | gsed ‘s/ /\n/g’
    a
    b
    c
    d

  2. Stumbled on this today. Late to the party, but for your case, there’s an alternative approach, which is cleaner, imo. Took the liberty of condensing your regex a bit for brevity:

    sed -i .bak ‘/^\$config_file = .*dev\/config.php”;$/ {
    s_.*_//&_
    a\
    $config_file = “dev/config.php”;
    }’ *.php

    A few notes: This uses pattern matching to only apply a block of commands to lines matching a particular pattern. So on lines that match the $config_file pattern, we do the substitution. Note that since we already know the line is the correct line, the actual search & replace pattern is much simpler: .* is the string to search. We can use & to represent the entirety of the match (the entire line). Also, although it’s typical to use / as the delimiter for the s command, you can pick almost any other character. Here, I’ve used _ to avoid having to escape the //. For the newline problem, we use the a\ command to explicitly add a new line to the output stream.

    Voila, \n issue resolved.

    Cheers.

  3. maybe you can lend a hand here – got an sed error on MAC

    bad flag in substitute command: ‘i’
    when using this simple regex -> s/in_ndoc/GS_NDOC/i
    on all other OSs this just runs.

    Cheers
    Frank

    1. Sorry Frank, apparently my comments have been in pending for months. I need to pay more attention! Looks like you were able to find a solution already ๐Ÿ™‚

  4. Sorry I think there was an answer:
    “To be clear: On OS X – as of Mountain Lion (10.8.2) – sed does NOT support case-insensitive matching – hard to believe, but true. The accepted answer gained that status because of the Perl-based solution mentioned in the comments.”

Leave a comment

Your email address will not be published. Required fields are marked *