Interactive search and replace from shell

KISS principle:

vim
:args `ls`
:argdo %s#SEARCH#REPLACE#gec |update

First character afer %s is used as separator


Just use Vim.

Start by using find to make a list of all files which need changing like so.

vim `find . -name '*.c' -exec grep junk {} \;`

That starts vim with all the .c files containing the string junk. Now, in vim make your change to the first file:

:%s/junk/rubbish/g

And then type :wEnter:nEnter for the next file.

Now you need to repeat the editing process. If it is only one substitute command and just a few files, then type :UpEnter to repeat the substitute command.

But if there are a lot, you should use map to create a couple of kestroke macros. The first macro would do the substitute command(s) and the second macro would do the w and n commands to save changes and get the next file.

I have used this (and older variants with vi) for the past 30 years. In particular, when you encode the two parts of this as two keystroke macros, you can run it very fast because it is easy to hold down the control key, and type a couple of letters, for instance R Y, over and over again. It is faster than the time it takes to write a fully automated script and since this is simple to reproduce, you can do it on any machine that you happen to be working on.


From jhvaras answer, I've made this bash command to quickly search and replace (to add to .bashrc):

replace () {
    if [ $# -lt 2 ]
    then
        echo "Recursive, interactive text replacement"
        echo "Usage: replace text replacement"
        return
    fi

    vim -u NONE -c ":execute ':argdo %s/$1/$2/gc | update' | :q" $(ag $1 -l)
}

It's used as follows:

~$ replace some_text some_new_text

It uses ag to search in advance, as it's probably faster than letting vim do the work, but you can probably substitute anything else you like. It also calls vim with no plugins for maximum speed, and after it has finished all substitutions it automatically quits and goes back to the shell.


I would add these modifications to Dillon's answer:

The -le option should be added to the grep command.

vim `find . -name '*.c' -exec grep -le '\<junk\>'  {} \;`

Then you are in Vim, but you don't have the opportunity to choose what to replace, add c option at the end for interactive replacements and bufdo at the beginning for walking through every file:

:bufdo %s/junk/rubbish/gce

Later you save all your work:

:bufdo wq!