sedxargsag

sed - Search and replace always puts capture group at the end


I'm refactoring some code across lots of files, and am trying to do a search and replace using sed on a Mac.

The goal is to go from this:

fooActions: typeof fooActionCreators

to this:

fooActions: Partial<typeof fooActionCreators>

I've gotten it to mostly work using this sed expression:

ag -l "Actions: typeof " | xargs sed -i "" -e "s/Actions: typeof \(\w*\)/Actions: Partial<typeof \1>/g"

Basically using ag to find the list of file names containing a matching string, then use xargs to pass those as input to my sed expression.

The output I get though looks like this: fooActions: Partial<typeof >fooActionCreators

I can't figure out why my capture group is being placed at then end instead of where I have it in the replace clause.


Solution

  • The sed that comes with mac doesn't support \w for word characters.

    If you have gsed (gnu sed) this should work as expected.

    $ echo 'Actions: typeof foo' |  gsed  -e "s/Actions: typeof \(\w*\)/Actions: Partial<typeof \1>/g"
    Actions: Partial<typeof foo>
    

    Otherwise you can use something like [a-zA-Z]* instead of \w*.

    $ echo 'Actions: typeof foo' |  sed  -e "s/Actions: typeof \([a-zA-Z]*\)/Actions: Partial<typeof \1>/g"
    Actions: Partial<typeof foo>
    

    The seemingly odd behavior that you're seeing is because in the plain sed version, the \w* is matching the empty string in front of the thing you're hoping to match, as illustrated below:

    Plain sed matches empty string in front of a

    $ echo a | sed "s/\(\w*\)/\1x/"
    xa
    

    Gnu sed matches the a as intended

    $ echo a | gsed "s/\(\w*\)/\1x/"
    ax