The Computer Oracle

How can I do a recursive find and replace from the command line?

--------------------------------------------------
Hire the world's top talent on demand or became one of them at Toptal: https://topt.al/25cXVn
and get $2,000 discount on your first invoice
--------------------------------------------------

Take control of your privacy with Proton's trusted, Swiss-based, secure services.
Choose what you need and safeguard your digital life:
Mail: https://go.getproton.me/SH1CU
VPN: https://go.getproton.me/SH1DI
Password Manager: https://go.getproton.me/SH1DJ
Drive: https://go.getproton.me/SH1CT


Music by Eric Matyas
https://www.soundimage.org
Track title: Puzzle Game 3

--

Chapters
00:00 How Can I Do A Recursive Find And Replace From The Command Line?
00:15 Accepted Answer Score 193
01:29 Answer 2 Score 4
01:50 Answer 3 Score 66
01:59 Answer 4 Score 8
02:30 Thank you

--

Full question
https://superuser.com/questions/428493/h...

--

Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...

--

Tags
#bash #shell #zsh #findandreplace

#avk47



ACCEPTED ANSWER

Score 193


This command will do it (tested on both Mac OS X Lion and Kubuntu Linux).

# Recursively find and replace in files
find . -type f -name "*.txt" -print0 | xargs -0 sed -i '' -e 's/foo/bar/g'

Here's how it works:

  1. find . -type f -name '*.txt' finds, in the current directory (.) and below, all regular files (-type f) whose names end in .txt
  2. | passes the output of that command (a list of filenames) to the next command
  3. xargs gathers up those filenames and hands them one by one to sed
  4. sed -i '' -e 's/foo/bar/g' means "edit the file in place, without a backup, and make the following substitution (s/foo/bar) multiple times per line (/g)" (see man sed)

Note that the 'without a backup' part in line 4 is OK for me, because the files I'm changing are under version control anyway, so I can easily undo if there was a mistake.

To avoid having to remember this, I use an interactive bash script, as follows:

#!/bin/bash
# find_and_replace.sh

echo "Find and replace in current directory!"
echo "File pattern to look for? (eg '*.txt')"
read filepattern
echo "Existing string?"
read existing
echo "Replacement string?"
read replacement
echo "Replacing all occurences of $existing with $replacement in files matching $filepattern"

find . -type f -name $filepattern -print0 | xargs -0 sed -i '' -e "s/$existing/$replacement/g"



ANSWER 2

Score 66


find . -type f -name "*.txt" -exec sed -i -e 's/foo/bar/g' {} +

This removes the xargs dependency.




ANSWER 3

Score 8


Try:

sed -i 's/foo/bar/g' $(find . -type f)

Tested on Ubuntu 12.04.

EDIT:

This command will NOT work if subdirectory names and/or filenames contain spaces, but if you do have them don't use this command as it won't work.

It is generally a bad practice to use spaces in directory names and filenames.

http://linuxcommand.org/lc3_lts0020.php

Look at "Important facts about file names"




ANSWER 4

Score 4


Here's my zsh/perl function I use for this:

change () {
        from=$1 
        shift
        to=$1 
        shift
        for file in $*
        do
                perl -i.bak -p -e "s{$from}{$to}g;" $file
                echo "Changing $from to $to in $file"
        done
}

And I'd execute it using

$ change foo bar **/*.java

(for example)