337

I run this command to find and replace all occurrences of 'apple' with 'orange' in all files in root of my site:

find ./ -exec sed -i 's/apple/orange/g' {} \; 

But it doesn't go through sub directories.

What is wrong with this command?

Here are some lines of output of find ./:

./index.php ./header.php ./fpd ./fpd/font ./fpd/font/desktop.ini ./fpd/font/courier.php ./fpd/font/symbol.php 
9
  • could you run find ./ and post some sample output? And the directory strucuture please. edit: thanks! Commented Jul 20, 2011 at 8:21
  • Hm your find is correct, works for me with subdirs. Commented Jul 20, 2011 at 8:31
  • 1
    How do you know it does not process subdirectories? Commented Jul 20, 2011 at 8:34
  • because it gives these errors: sed: couldn't edit ./fpd: not a regular file sed: couldn't edit ./fpd/font: not a regular file sed: couldn't edit ./fpd/font/makefont: not a regula Commented Jul 20, 2011 at 8:42
  • oh... i grep for apple and nothing found.they all were replaced. ;) thank you . you opened my eyes !!! Commented Jul 20, 2011 at 8:43

8 Answers 8

576

Your find should look like that to avoid sending directory names to sed:

find ./ -type f -exec sed -i -e 's/apple/orange/g' {} \; 
Sign up to request clarification or add additional context in comments.

11 Comments

You may need to change sed -i 's/apple/orange/g' to sed -i '' 's/apple/orange/g' to make this work.
-i takes an argument: the extension used to save the temporary file. In GNU sed, looks like there's no space between -i and its argument, but in BSD sed there is… so BSD -i '' 's/foo/bar/' is equivalent to GNU -i 's/foo/bar/.
Actually adding -e does not work on Mac OS. touch a b c d e followed by the command above produces a directory listing like this: a a-e b b-e c c-e d d-e e e-e.
For Mac OS, this answers stackoverflow.com/questions/19242275/… the RE error: illegal byte sequence
For fish shell users, be sure to quote the empty braces '{}', because fish automatically expands the empty braces if not quoted.
|
141

For larger s&r tasks it's better and faster to use grep and xargs, so, for example;

grep -rl 'apples' /dir_to_search_under | xargs sed -i 's/apples/oranges/g' 

4 Comments

Thanks for this answer, it was very helpful! If in a git repository, it's even faster using git grep -l 'apples' | xargs sed -i 's/apples/oranges/g'
If on macos, use xargs sed -i '' 's/apples/oranges/g'
If you're trying to replace something with a forward slash in it, you can use sed with | rather than /, e.g. ... xargs sed -i 's|mduuid/apples|mduuid/oranges|g' stackoverflow.com/questions/40714970/…
This fails in xargs 4.8.0. It produces the error unmatched single quote: by defaults quotes are special to xargs unless you use the -0 option
14

Since there are also macOS folks reading this one (as I did), the following code worked for me (on 10.14)

egrep -rl '<pattern>' <dir> | xargs -I@ sed -i '' 's/<arg1>/<arg2>/g' @ 

All other answers using -i and -e do not work on macOS.

Source

1 Comment

On the mac the accepted answer does work [kind of] - but it spits out 'duplicate' files with <original-filename>-e which would need to be removed / piped into another command (to use verbatim). This method is better though and is still working for me (on 11.2.3)
7

This worked for me:

find ./ -type f -exec sed -i '' 's#NEEDLE#REPLACEMENT#' *.php {} \; 

3 Comments

original question doesn't restrict to *.php files, there's also an .ini one
The unquoted *.php is really incorrect; you just got lucky that it didn't get expanded in the starting directory because you didn't happen to have any matching files there.
Restriction on file name could be obtained with -name *.php on the find command.
5
grep -e apple your_site_root/**/*.* -s -l | xargs sed -i "" "s|apple|orange|" 

Comments

1

Found a great program for this called ruplacer

https://github.com/dmerejkowsky/ruplacer

Usage

ruplacer before_text after_text # prints out list of things it will replace ruplacer before_text after_text --go # executes the replacements 

It also respects .gitignore so it won't mess up your .git or node_modules directories (find . by default will go into your .git directory and can corrupt it!!!)

Comments

0

I think we can do this with one line simple command

for i in `grep -rl eth0 . 2> /dev/null`; do sed -i ‘s/eth0/eth1/’ $i; done 

Refer to this page.

Comments

-7

In linuxOS:

sed -i 's/textSerch/textReplace/g' namefile 

if "sed" not work try :

perl -i -pe 's/textSerch/textReplace/g' namefile 

1 Comment

he wants to find all files in sub directories contain that string and replace, not only a single file

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.