- Replace a string with another string in all files below my current dir
- 6 Answers 6
- Find and Replace string in all files recursive using grep and sed [duplicate]
- 5 Answers 5
- How to Use Sed to Find and Replace a String in a File
- sed Find and Replace Syntax
- sed Replace Examples
- Replace First Matched String
- Global Replace
- Match and Replace All Cases
- Ignore Substrings
- Find and Replace Strings With Slashes
- Find and Replace with Regular Expressions
- Reference Found String
- Create a Backup
- Recursive Find and Replace
- How can I do a recursive find/replace of a string with awk or sed?
- 37 Answers 37
Replace a string with another string in all files below my current dir
How do I replace every occurrence of a string with another string below my current directory? Example: I want to replace every occurrence of www.fubar.com with www.fubar.ftw.com in every file under my current directory. From research so far I have come up with
sed -i 's/www.fubar.com/www.fubar.ftw.com/g' *.php
6 Answers 6
You’re on the right track, use find to locate the files, then sed to edit them, for example:
find . -name '*.php' -exec sed -i -e 's/www.fubar.com/www.fubar.ftw.com/g' <> \;
- The . means current directory — i.e. in this case, search in and below the current directory.
- For some versions of sed you need to specify an extension for the -i option, which is used for backup files.
- The -exec option is followed by the command to be applied to the files found, and is terminated by a semicolon, which must be escaped, otherwise the shell consumes it before it is passed to find.
@KRB: .bak is for backup in case you change something you didn’t want to change. <> is substituted by find command with file name when executing a command by -exec .
@KRB: in regex’s the letters after last slash are options. Here ‘/g’ stands for «do global» substitutions (all in a line, not only first).
Solution using find, args and sed:
find . -name '*.php' -print0 | xargs -0 sed -i 's/www.fubar.com/www.fubar.ftw.com/g'
It uses one process more, but represents a more general pattern which is very useful in its own right; i.e. generate-args | xargs command
xargs builds a command line and tries to run the command after as few times as possible, exec runs the command once for every file. -print0 and -0 takes care of filenames with spaces in them.
I think this is a good solution when running in a Windows environment where file names and folder names may have space characters in them. I find myself constantly having to use the -print0 | xargs -r0 arg combo.
The suggested solution worked great. Except for one gotcha: It replaces symlinks with a file of the target’s contents.
#!/bin/bash shopt -s nullglob for file in *.php do while read -r line do echo "$" done < "$file" >tempo && mv tempo "$file" done
A more efficient * alternative to the currently accepted solution:
`grep "www.fubar.com" . -lr | xargs sed -i 's/www.fubar.com/www.fubar.ftw.com/g'
This avoids the inefficiency of the find . -exec method, which needlessly runs a sed in-place replacement over all files below your current directory regardless of if they contain the string you’re looking for or not, by instead using grep -lr . This gets just the files containing the string you want to replace which you can then pipe to xargs sed -i to perform the in-place replacement on just those files.
* : I used time to make a cursory comparison of my method with the accepted solution (adapted for my own use case); The find . -exec -style method took 3.624s to run on my machine and my above proposed solution took 0.156s, so roughly 23x faster for my use case.
Find and Replace string in all files recursive using grep and sed [duplicate]
You’ll also have an issue with the / in your patterns. They should be escaped. Or you could use another delimiter for your s command (e.g. @ ).
5 Answers 5
As @Didier said, you can change your delimiter to something other than / :
grep -rl $oldstring /path/to/folder | xargs sed -i s@$oldstring@$newstring@g
The above would fail in SO many interesting ways given so many possible contents of oldstring or newstring.
@EdMorton, would you please put an example of how does it fail? I know you posted the traditional form of doing below, but just saying it can fail leave the question of how. Then I could understand why I should your solution. These solution just seems much more easy. Thanks
Try the above with any shell globbing character or an @ in either string variable. The globbing chars can/will match file names in your directory, if not today then some day in future when you last expect it, and the @ will terminate your sed command. It’ll also fail if oldstring contains spaces in various locations, not to mention if either string contains newline characters. The OP specifically said the «oldstring» has special characters so that’s a big heads up that any of what I just described and more is likely to happen.
Also take care that in some environments, notably Darwin/MacOSX you need to give an explicit backup extension like ‘sed -i .bak’.
grep -rl $oldstring . | xargs sed -i "s/$oldstring/$newstring/g"
So the complication with this, is files with spaces in their names won’t be picked up. If you had «/files/File Two.txt», sed would try «/files/File» and «Two.txt».
The GNU guys REALLY messed up when they introduced recursive file searching to grep. grep is for finding REs in files and printing the matching line (g/re/p remember?) NOT for finding files. There’s a perfectly good tool with a very obvious name for FINDing files. Whatever happened to the UNIX mantra of do one thing and do it well?
Anyway, here’s how you’d do what you want using the traditional UNIX approach (untested):
find /path/to/folder -type f -print | while IFS= read -r file do awk -v old="$oldstring" -v new="$newstring" ' BEGIN < rlength = length(old) >rstart = index($0,old) < $0 = substr($0,rstart-1) new substr($0,rstart+rlength) > < print >' "$file" > tmp && mv tmp "$file" done
Not that by using awk/index() instead of sed and grep you avoid the need to escape all of the RE metacharacters that might appear in either your old or your new string plus figure out a character to use as your sed delimiter that can’t appear in your old or new strings, and that you don’t need to run grep since the replacement will only occur for files that do contain the string you want. Having said all of that, if you don’t want the file timestamp to change if you don’t modify the file, then just do a diff on tmp and the original file before doing the mv or throw in an fgrep -q before the awk.
Caveat: The above won’t work for file names that contain newlines. If you have those then let us know and we can show you how to handle them.
How to Use Sed to Find and Replace a String in a File
The sed (stream editor) utility is a line-oriented text parsing and transformation tool. The sed command uses a simple programming language and regular expressions to process text streams and files. The most used feature of the sed command is string substitution.
This guide shows how to use sed to find and replace strings through examples.
- Access to the command line/terminal.
- A text file (this guide provides an example.txt file).
- Basic terminal commands (grab our free cheat sheet).
sed Find and Replace Syntax
The syntax to find and replace text using the sed command is:
The command consists of the following:
- -i tells the sed command to write the results to a file instead of standard output.
- s indicates the substitute command.
- / is the most common delimiter character. The command also accepts other characters as delimiters, which is useful when the string contains forward slashes.
- is the string or regular expression search parameter.
- is the replacement text.
- g is the global replacement flag, which replaces all occurrences of a string instead of just the first.
- is the file where the search and replace happens.
The single quotes help avoid meta-character expansion in the shell.
The BDS version of sed (which includes macOS) does not support case-insensitive matching or file replacement. The command for file replacement looks like this:
Alternatively, install the GNU version of sed on macOS with homebrew:
Run the GNU sed command as follows:
Replace the sed command with gsed to follow the examples below.
sed Replace Examples
The examples from this guide use a sample file to replace strings.
1. Create a sample text file:
2. Add the following contents:
foobarbazfoobarbaz foo bar baz foo bar baz Foo Bar Baz Foo Bar Baz FOO BAR BAZ FOO BAR BAZ /foo/bar/baz /foo/bar/baz
Use the file as input to test the examples below.
Note: For a full sed command tutorial for Linux, check out our sed command Linux guide.
Replace First Matched String
1. To replace the first found instance of the word bar with linux in every line of a file, run:
sed -i 's/bar/linux/' example.txt
2. The -i tag inserts the changes to the example.txt file. Check the file contents with the cat command:
The command replaces the first instance of bar with linux in every line, including substrings. The match is exact, ignoring capitalization variations.
Global Replace
To replace every string match in a file, add the g flag to the script. For example:
sed -i 's/bar/linux/g' example.txt
The command globally replaces every instance of bar with linux in the file.
Match and Replace All Cases
To find and replace all instances of a word and ignore capitalization, use the I parameter:
sed -i 's/bar/linux/gI' example.txt
The command replaces all instances of the word bar in the text, ignoring capitalization.
Ignore Substrings
Add word boundaries ( \b ) to the sed command to ignore substrings when replacing strings in a file. For example:
sed -i 's/\bbar\b/linux/gI' example.txt
Alternatively, change the delimiter to make the command easier to read:
sed -i 's:\bbar\b:linux:gI' example.txt
The command ignores substrings, matching only the whole word.
Find and Replace Strings With Slashes
Escape the forward slash character to find and replace a string with slashes. For example, to replace /foo/bar/baz with /home/kb, use the following syntax:
sed -i 's/\/foo\/bar\/baz/\/home\/kb/g' example.txt
Alternatively, change the delimiter to avoid escaping characters:
sed -i 's:/foo/bar/baz:/home/kb:g' example.txt
Use this syntax to replace paths and other strings with slashes.
Find and Replace with Regular Expressions
The search pattern for the sed command accepts regular expressions, similar to grep regex. For example, to match all capital letters and replace them with 5, use:
The regex pattern helps find all capital letters and replaces them with the number in the file.
Reference Found String
Use the ampersand character ( & ) to reference the found string. For example, to add a forward slash (/) before every instance of foo in a file, use:
Instead of retyping the search parameter, the & sign references the found string.
Create a Backup
To create a backup file before overwriting the existing one, add the .bak parameter to the -i tag.
sed -i.bak 's/foo/FOO/g' example.txt
The command creates a backup ( example.txt.bak ) before overwriting the original. Use this method to keep a copy in the original format and avoid overwriting.
Recursive Find and Replace
Use the find command to search for files and combine it with sed to replace strings in files recursively. For example:
find -name 'example*' -exec sed -i 's/[a-z]/5/gI' <> +
The command finds all files starting with example and executes the sed command on the files. The executed command replaces all letters with 5, ignoring capitalization.
After going through the examples in this guide, you know how to use sed to replace strings in files. The sed command is a powerful text manipulation utility with many advanced features.
Next, check out the awk or gawk command to learn about other text manipulation tools.
How can I do a recursive find/replace of a string with awk or sed?
oh my god this is exactly what I just did. But it worked and doesn’t seem to have done any harm. Whats the worst that could happen?
Quick tip for all the people using sed: It will add trailing newlines to your files. If you don’t want them, first do a find-replace that won’t match anything, and commit that to git. Then do the real one. Then rebase interactively and delete the first one.
You can exclude a directory, such as git, from the results by using -path ./.git -prune -o in find . -path ./.git -prune -o -type f -name ‘*matchThisText*’ -print0 before piping to xargs
37 Answers 37
find /home/www \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g'
-print0 tells find to print each of the results separated by a null character, rather than a new line. In the unlikely event that your directory has files with newlines in the names, this still lets xargs work on the correct filenames.
\( -type d -name .git -prune \) is an expression which completely skips over all directories named .git . You could easily expand it, if you use SVN or have other folders you want to preserve — just match against more names. It’s roughly equivalent to -not -path .git , but more efficient, because rather than checking every file in the directory, it skips it entirely. The -o after it is required because of how -prune actually works.
For more information, see man find .