Skip to content

Instantly share code, notes, and snippets.

@hlissner
Last active September 11, 2023 10:14
Show Gist options
  • Star 77 You must be signed in to star a gist
  • Fork 16 You must be signed in to fork a gist
  • Save hlissner/db74d23fc00bed81ff62 to your computer and use it in GitHub Desktop.
Save hlissner/db74d23fc00bed81ff62 to your computer and use it in GitHub Desktop.
Bulk search & replace with ag (the_silver_searcher)
# ag <https://github.com/ggreer/the_silver_searcher>
# usage: ag-replace.sh [search] [replace]
# caveats: will choke if either arguments contain a forward slash
# notes: will back up changed files to *.bak files
ag -0 -l $1 | xargs -0 perl -pi.bak -e "s/$1/$2/g"
# or if you prefer sed's regex syntax:
ag -0 -l $1 | xargs -0 sed -ri.bak -e "s/$1/$2/g"
@dennislysenko
Copy link

If you add -0 right after xargs, it can also handle files with spaces in the filename ;)

@graingert
Copy link

graingert commented May 5, 2017

@dennislysenko you need the -0 in both ag and xargs

ag -0 -l $1 | xargs -0 sed -ri.bak -e "s/$1/$2/g"

@aaronmswan
Copy link

For what it's worth, this can be added as a function to .bashrc as

function agr { ag -0 -l "$1" | xargs -0 perl -pi.bak -e "s/$1/$2/g"; }
export -f agr

@KevOrr
Copy link

KevOrr commented May 3, 2018

Even better so you don't have to worry about slashes:

function agr { ag -0 -l "$1" | AGR_FROM="$1" AGR_TO="$2" xargs -r0 perl -pi -e 's/$ENV{AGR_FROM}/$ENV{AGR_TO}/g'; }

There could be some incompatibilities between the two regex languages though.

@ixis-kyle
Copy link

@KevOrr,
function agr { ag -0 -l "$1" | AGR_FROM="$1" AGR_TO="$2" xargs -r0 perl -pi -e 's/$ENV{AGR_FROM}/$ENV{AGR_TO}/g'; } has typo. Should be:
function agr { ag -0 -l "$1" | AGR_FROM="$1" AGR_TO="$2" xargs -0 perl -pi -e 's/$ENV{AGR_FROM}/$ENV{AGR_TO}/g'; }.

at least thats what worked for me, I honestly dont know much about the -0 argument.

@NightMachinery
Copy link

@ixis-kyle It’s correct, and necessary:

-r, --no-run-if-empty
              If  the  standard input does not contain any nonblanks,
              do not run the command.  Normally, the command  is  run
              once  even  if there is no input.  This option is a GNU
              extension.

@NightMachinery
Copy link

NightMachinery commented Jul 29, 2019

(See latest version https://gist.github.com/NightMachinary/719ec06299c65b5216d551658b78c951)

comment() { 
}
doc() { 
}
function agr { 
doc 'usage: from=sth to=another agr [ag-args]'
comment -l --files-with-matches

ag -0 -l "$from" "${@}" | pre-files "$from" "$to"
}
pre-files() {
doc 'stdin should be null-separated list of files that need replacement; $1 the string to replace, $2 the replacement.'
comment '-i backs up original input files with the supplied extension (leave empty for no backup; needed for in-place replacement.)(do not put whitespace between -i and its arg.)'
comment '-r, --no-run-if-empty
              If  the  standard input does not contain any nonblanks,
              do not run the command.  Normally, the command  is  run
              once  even  if there is no input.  This option is a GNU
              extension.'

AGR_FROM="$1" AGR_TO="$2" xargs -r0 perl -pi.pbak -e 's/$ENV{AGR_FROM}/$ENV{AGR_TO}/g'
}

You can use it like this:

from=str1 to=sth agr path1 path2 ...

Supply no paths to make it use the current directory.
Note that ag, xargs, and perl need to be installed and on PATH.

@nikisix
Copy link

nikisix commented Apr 17, 2020

I had to use ag's --nocolor argument for it go work

@devinrhode2
Copy link

devinrhode2 commented Oct 4, 2022

This worked well for me:

ag -0 -l 'Old' | xargs -0 sed -ri.bak -e 's/Old/New/g'; git clean -f '**/*.bak';

@liufeimath
Copy link

Even better so you don't have to worry about slashes:

function agr { ag -0 -l "$1" | AGR_FROM="$1" AGR_TO="$2" xargs -r0 perl -pi -e 's/$ENV{AGR_FROM}/$ENV{AGR_TO}/g'; }

There could be some incompatibilities between the two regex languages though.

I found a way to make this even work for multi-line find-and-replace: add 'BEGIN{undef $/;} ' to the perl -e command and then it'll do multi-line work:
function agr { ag -0 -l "$1" | AGR_FROM="$1" AGR_TO="$2" xargs -r0 perl -pi -e 'BEGIN{undef $/;} s/$ENV{AGR_FROM}/$ENV{AGR_TO}/g'; }

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