321

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
Zoe
  • 27,060
  • 21
  • 118
  • 148
hd.
  • 17,596
  • 46
  • 115
  • 165
  • could you run `find ./` and post some sample output? And the directory strucuture please. edit: thanks! – Jacob Jul 20 '11 at 08:21
  • Hm your find is correct, works for me with subdirs. – Jacob Jul 20 '11 at 08:31
  • 1
    How do you know it does not process subdirectories? – carlpett Jul 20 '11 at 08: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 – hd. Jul 20 '11 at 08:42
  • oh... i grep for apple and nothing found.they all were replaced. ;) thank you . you opened my eyes !!! – hd. Jul 20 '11 at 08:43
  • Possible duplicate of [Awk/Sed: How to do a recursive find/replace of a string?](http://stackoverflow.com/questions/1583219/awk-sed-how-to-do-a-recursive-find-replace-of-a-string) – tripleee Feb 18 '16 at 06:40
  • If using zsh, you can use e.g. `src/**/.js`. – alex Jul 18 '16 at 08:28
  • Answer you can find here: https://stackoverflow.com/a/49364510/5578292 – Yahor M Mar 19 '18 at 13:51
  • The error messages are unnerving, but the command actually does what you want. It's not correct to say it "doesn't work", though it's legitimate and useful to ask how to do this without those warning messages. – tripleee Jul 21 '19 at 12:00

8 Answers8

552

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

find ./ -type f -exec sed -i -e 's/apple/orange/g' {} \;
Philippe Fanaro
  • 6,148
  • 6
  • 38
  • 76
jfg956
  • 16,077
  • 4
  • 26
  • 34
  • 26
    You may need to change `sed -i 's/apple/orange/g'` to `sed -i '' 's/apple/orange/g'` to make this work. – paulmelnikow Nov 27 '13 at 00:20
  • 10
    `-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/`. – paulmelnikow Feb 18 '14 at 22:03
  • 29
    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`. – paulmelnikow Mar 10 '14 at 05:22
  • 10
    For Mac OS, this answers http://stackoverflow.com/questions/19242275/re-error-illegal-byte-sequence-on-mac-os-x the `RE error: illegal byte sequence` – kakoma Aug 14 '17 at 21:27
  • 5
    For fish shell users, be sure to quote the empty braces ```'{}'```, because fish automatically expands the empty braces if not quoted. – Kevin Cherepski Apr 05 '18 at 16:47
  • 1
    Is there are shorter version of this? Pretty lengthy. Or a way to make it an alias/bash function? – Colin D Apr 19 '18 at 19:49
  • 4
    Can someone please explain what is the -exec and the {} \; at the end? – quantum231 Aug 30 '21 at 15:44
  • could this be turned into a shortened alias? – Colin D Jan 19 '22 at 01:40
  • Can someone else explain the answer to @quantum231's question about what the `{} \;` is doing? The single word explanation isn't quite getting the concept across for me. – Lou Aug 03 '23 at 16:30
128

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'
Julius
  • 1,379
  • 1
  • 8
  • 7
  • 34
    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'` – mrodrigues Jul 11 '16 at 21:16
  • 2
    If on macos, use `xargs sed -i '' 's/apples/oranges/g'` – cacti5 Feb 04 '21 at 23:21
  • 2
    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'` https://stackoverflow.com/questions/40714970/escaping-forward-slashes-in-sed-command – NessDan Mar 03 '22 at 01:24
  • 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` – bob Aug 15 '22 at 02:24
13

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

pat-s
  • 5,992
  • 1
  • 32
  • 60
  • On the mac the accepted answer does work [kind of] - but it spits out 'duplicate' files with -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) – bob dylan Sep 24 '21 at 11:30
6

This worked for me:

find ./ -type f -exec sed -i '' 's#NEEDLE#REPLACEMENT#' *.php {} \;
shadyyx
  • 15,825
  • 6
  • 60
  • 95
blackdad
  • 1,343
  • 1
  • 10
  • 13
  • original question doesn't restrict to *.php files, there's also an .ini one – knocte Aug 10 '18 at 06:52
  • 2
    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. – tripleee Jul 21 '19 at 11:58
  • 1
    Restriction on file name could be obtained with `-name *.php` on the `find` command. – aleric Jan 08 '21 at 09:35
4
grep -e apple your_site_root/**/*.* -s -l | xargs sed -i "" "s|apple|orange|"
Wazery
  • 15,394
  • 19
  • 63
  • 95
rocLv
  • 548
  • 6
  • 15
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!!!)

Colin D
  • 2,822
  • 1
  • 31
  • 38
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.

Billal Begueradj
  • 20,717
  • 43
  • 112
  • 130
Sukrant
  • 9
  • 2
-7

In linuxOS:

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

if "sed" not work try :

perl -i -pe 's/textSerch/textReplace/g' namefile
  • 4
    he wants to find all files in sub directories contain that string and replace, not only a single file – phuclv Jun 09 '17 at 09:58