Skip to content

Instantly share code, notes, and snippets.

@jcamenisch
Created January 24, 2012 19:19
Show Gist options
  • Save jcamenisch/1671995 to your computer and use it in GitHub Desktop.
Save jcamenisch/1671995 to your computer and use it in GitHub Desktop.
Lightning-fast project-wide find/replace with git grep and sed
gg_replace() {
if [[ "$#" == "0" ]]; then
echo 'Usage:'
echo ' gg_replace term replacement file_mask'
echo
echo 'Example:'
echo ' gg_replace cappuchino cappuccino *.html'
echo
else
find=$1; shift
replace=$1; shift
ORIG_GLOBIGNORE=$GLOBIGNORE
GLOBIGNORE=*.*
if [[ "$#" = "0" ]]; then
set -- ' ' $@
fi
while [[ "$#" -gt "0" ]]; do
for file in `git grep -l $find -- $1`; do
sed -e "s/$find/$replace/g" -i'' $file
done
shift
done
GLOBIGNORE=$ORIG_GLOBIGNORE
fi
}
gg_dasherize() {
gg_replace $1 `echo $1 | sed -e 's/_/-/g'` $2
}
@glasser
Copy link

glasser commented Jul 25, 2013

Odd, I get (on Mac) sed: -i may not be used with stdin. Changing -i'' to -i '' (with a space) fixes this, which makes sense: -i'' is exactly equivalent to -i, I think, so it's treating $file as the extension.

@dawalama
Copy link

Here is my version that I use on Mac that handles filenames with 'Spaces'.

gg_replace() {
    if [[ "$#" == "0" ]]; then
        echo 'Usage:'
        echo '  gg_replace term replacement file_mask'
        echo
        echo 'Example:'
        echo '  gg_replace cappuchino cappuccino *.html'
        echo
    else
        find=$1; shift
        replace=$1; shift

        ORIG_GLOBIGNORE=$GLOBIGNORE
        GLOBIGNORE=*.*

        if [[ "$#" = "0" ]]; then
            set -- ' ' $@
        fi

        while [[ "$#" -gt "0" ]]; do
            for file in `git grep -l $find -- $1 | sed -e "s/ /~~~~~/g"`; do
                sed -i "" -e "s/$find/$replace/g" "${file/~~~~~/ }"
            done
            shift
        done

        GLOBIGNORE=$ORIG_GLOBIGNORE
    fi
}

@brunoro
Copy link

brunoro commented Oct 7, 2015

just a small tip for those trying to use this on OS X: install GNU sed (brew install gnu-sed) and replace the sed call with gsed

@glyph
Copy link

glyph commented Oct 16, 2020

Here's my version: https://gist.github.com/glyph/9beafa8a7b26e5ca9f6666448fa5810d

gg_replace() {
    if [[ "$#" -lt "2" ]]; then
        echo "
Usage:
  $0 term replacement file_mask
Example:
  $0 cappuchino cappuccino '*.html'
";
    else
        local find="$1"; shift;
        local replace="$1"; shift;
        git grep -zlI "${find}" -- "$@" |
            xargs -0 sed -e "s/${find}/${replace}/g" -i '' ;
    fi;
}

A few notes:

  • passes shellcheck
  • doesn't depend on word splitting so doesn't need to monkey with GLOBIGNORE
  • supports spaces, newlines, and (apropos of dawalama's version ;-)) ~~~~~ in path names
  • it still has the problem described in dwheeler's https://dwheeler.com/essays/filenames-in-shell.html section 3.3, since I couldn't figure out a quick way to make git grep prefix all its output with ./
  • it's a bit faster since it doesn't spawn new sed processes quite so many times

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment