Global Linux command line lookup and replacement

I am trying to find and replace a string in all files mapped by grep on a linux machine. I have some parts of what I want to do, but I'm not sure how best to tie them all together.

grep -n 'foo' * will give me output in the form:

 [filename]:[line number]:[text] 

For each file returned by grep, I would like to replace "foo" with "bar" and write the result back to the file. Is there a good way to do this? Maybe a fantastic pipeline?

+78
linux grep awk sed
Jan 22 '09 at 22:54
source share
8 answers

Do you want to find and replace the string in all files matching grep?

 perl -p -i -e 's/oldstring/newstring/g' `grep -ril searchpattern *` 

Edit

Since this seems to be a pretty popular question that I would update.

Currently, I mainly use ack-grep as it is more user friendly. Thus, the specified command will be:

 perl -p -i -e 's/old/new/g' `ack -l searchpattern` 

To handle spaces in file names, you can run:

 ack --print0 -l searchpattern | xargs -0 perl -p -i -e 's/old/new/g' 

you can do more with ack-grep . Suppose you want to limit your search to HTML files only:

 ack --print0 --html -l searchpattern | xargs -0 perl -p -i -e 's/old/new/g' 

And if space is not a problem, it is even shorter:

 perl -p -i -e 's/old/new/g' `ack -l --html searchpattern` perl -p -i -e 's/old/new/g' `ack -f --html` # will match all html files 
+67
Jan 22 '09 at 22:57
source share

This is similar to what you want based on the example you provided:

 sed -i 's/foo/bar/g' * 

This is not recursive (it will not drop into subdirectories). For a good solution, replacing the selected files in a tree, I would use find:

 find . -name '*.html' -print -exec sed -i.bak 's/foo/bar/g' {} \; 

*.html is an expression that must match the files, .bak after -i makes a copy of the source file with the extension .bak (it can be any extension you like) and g at the end of the sed expression tells sed to replace several copies with one line (and not just the first). -print for search is a convenience to show which files were mapped. It all depends on the exact versions of these tools on your system.

+105
Jan 23 '09 at 1:41
source share

If your sed(1) has the -i option, then use it like this:

 for i in *; do sed -i 's/foo/bar/' $i done 

If not, there are several options for changing depending on which language you want to play:

 ruby -i.bak -pe 'sub(%r{foo}, 'bar')' * perl -pi.bak -e 's/foo/bar/' * 
+13
Jan 22 '09 at 22:59
source share

I like and used the above solution or search in the system and replace among thousands of files:

 find -name '*.htm?' -print -exec sed -i.bak 's/foo/bar/g' {} \; 

I guess with '* .htm?' instead of .html, it searches and finds .htm and .html files.

I am replacing .bak with a more used tilde (~) system to make cleaning up my backup files easier.

+6
Jul 20 '10 at 18:14
source share

This works using grep without using perl or find.

 grep -rli 'old-word' * | xargs -i@ sed -i 's/old-word/new-word/g' @ 
+4
Dec 21 '13 at 16:58
source share

find . -type f -print0 | xargs -0 <sed/perl/ruby cmd> find . -type f -print0 | xargs -0 <sed/perl/ruby cmd> will handle multiple spaces containing file names, immediately loading one interpreter per package. Much faster.

+3
Dec 01 '11 at 13:40
source share

The answer already used when using find and sed

find -name '*.html' -print -exec sed -i.bak 's/foo/bar/g' {} \;

probably is the standard answer. Or you can use perl -pi -es/foo/bar/g' instead of the sed command.

For the quickest use, you may find the rpl command easier to remember. Here is the replacement (foo โ†’ bar), recursively to all files in the current directory:

rpl -R foo bar .

It is not available by default for most Linux distributions, but it installs quickly ( apt-get install rpl or similar).

However, for more complex tasks, which include regular expressions and backward substitution, or file renaming, as well as searching and replacing, the most common and powerful tool I know about is repren , a little Python script I wrote some time ago for some more complex renaming and refactoring tasks. The reasons you might prefer this are:

  • Support for renaming files, as well as finding and replacing file contents (including moving files between directories and creating new parent directories).
  • Please review the changes before performing a search and replacement.
  • Support regular expressions with backward substitution, in whole words, case insensitive and case preservation (replace foo โ†’ bar, Foo โ†’ Bar, FOO โ†’ BAR).
  • It works with several replacements, including swaps (foo โ†’ bar and bar โ†’ foo) or sets of unique replacements (foo โ†’ bar, f โ†’ x).

Check out the README example .

+1
Mar 22 '15 at 6:15
source share

This is actually easier than it sounds.

 grep -Rl 'foo' ./ | xargs -n 1 -I % sh -c "ls %; sed -i 's/foo/bar/g' %"; 
  • grep recurses through your tree (-R) and prints only the file name (-l), starting from the current directory (./)
  • which is sent through the xargs pipes, which processes them one at a time (-n 1) and uses% as a placeholder (-I%) in the shell command (sh -c)
  • on the command line, the file name is first printed (ls%;)
  • then sed performs the built-in operation (-i), substituting ('s /') foo with the bar (foo / bar), globally (/ g) in the file (again, represented by%)

Easy peasy. If you are well versed in searching grep, xargs, sed and awk, almost nothing happens when it comes to processing text files in bash :)

+1
Nov 19 '15 at 3:35
source share



All Articles