Unix/Linux Find & Replace in Multiple Files
February 1, 2008 – 11:00 pm by Gabe Anderson | 37 CommentsHere’s a classic that I originally published 20 February 2003 – 11:14. This entry was the #1 most popular post between 11/19/2005 and 11/13/2006, receiving 16,443 page views (13,653 unique) during that time.
Since it’s damn near impossible to find online the simplest way to scan a Unix directory of files, search for one text pattern, and replace with another, I am now archiving the simplest method I could find (which I’ve tested and have proven that it works beautifully). Simply cd to the directory where your files live, modify (or leave) the *.php to match the file type you are modifying, then run the following at the command line:
-
for fl in *.php; do
mv $fl $fl.old
sed ‘s/FINDSTRING/REPLACESTRING/g’ $fl.old > $fl
#rm -f $fl.old
done
Uncomment rm -f $fl.old if you don’t want to bother keeping a copy of the old files. Simple, eh? It’s all about sed, baby.
Linux: Replace a string in several text files
[ Subscribe to gabeanderson.com via email or RSS feed. ]
37 Responses to “Unix/Linux Find & Replace in Multiple Files”
Awesome little script; thanks for sharing.
By Indrid Cold on Apr 25, 2003
Thanks a lot for this. I’ve been looking for a way to do this for quite a while. I’m suprised its not a function of sed or a built in linux command.
Now if only you can make this recursive through subdirectories!
By Matt Toledo on Jun 10, 2003
The following script includes subdirectories and capsulates the functionality in a file.
first arg: File Pattern; e.g ‘*.php’
second arg: pattern to search for; e.g. “find string”
third arg: replace string; e.g. “replace string”
The script still fails on filenames, that contain spaces. Any ideas what’s wrong?
=============================================
Paste this into a file ‘renall’ and make it executable (chmod u+x renall):
#!/bin/sh
if [ $# -lt 3 ] ; then
echo -e “Wrong number of parameters.”
echo -e “Usage:”
echo -e ” renall ‘filepat’ findstring replacestring\n”
exit 1
fi
#echo $1 $2 $3
for i in `find . -name “$1” -exec grep -l “$2” {} \;`
do
mv “$i” “$i.sedsave”
sed “s/$2/$3/g” “$i.sedsave” > “$i”
echo $i
#rm “$i.sedsave”
done
By Tobias on Jul 17, 2003
Sorry, I’m new to Linux.
I just can’t seem to get it to work. I copy it to the directory, chmod it, and.. well, I guess my question is if I’m running it properly. Should there be any output?
By sean on Jul 18, 2003
Ah, nevermind. I got it to work. Thanks.
By sean on Jul 18, 2003
Thank you. I had previously found a solution that uses a similar syntax in Perl (in the Linux Cookbook), but it fails to escape characters properly. Your script works perfectly.
By Sleeper on Jul 29, 2003
Thanks for sharing this … was very useful.
By Harish Puranik on Sep 3, 2003
There is a nice little utility called rpl for this task. http://software.freshmeat.net/projects/rpl/
By Gergo on Oct 26, 2003
That rpl utility was very useful…it did exactly what I needed. Thanks!
By Michael Sewell on Nov 11, 2003
Useful little script, thanks 🙂
By Mario on Nov 24, 2003
Thank you, thank you, thank you!
Just what I needed.
By Aleda Freeman on Dec 3, 2003
This is great! Thanks for the tip!
By Grant on Jan 16, 2004
Nice little script, thanks. 🙂
By Nick Gushlow on Feb 11, 2004
Wonderful! Thanks!
By Mandy on Apr 21, 2004
Just what I was looking for! THX!
By Sway on Apr 21, 2004
Wow! How easy. Thanks!!
By Megan on May 4, 2004
The following works for me. The command on the web page you reference has an error (see my comment there). It should be:
find ./ -type f -exec sed ‘s/string1/string2/’ {} \;
This command visits every file in . and it’s subdirectories and makes the substitution.
By scott on May 22, 2004
oops, forgot the -i option to make sed edit the file in-place:
find ./ -type f -exec sed -i ‘s/string1/string2/’ {} \;
By scott on May 22, 2004
Thanks a bunch! This is exactly what I needed 🙂 Will save me tons of repetitive, mindless work heh
By KristyX on Nov 4, 2004
Be sure you replace any backticks (the character next to the number 1 on a keyboard) with single quotes. Certain WordPress themes like to replace these.
By Johntron on Mar 27, 2008
re that script:
if [ $# -lt 3 ] ; then
echo -e “Wrong number of parameters.”
echo -e “Usage:”
echo -e ” renall ‘filepat’ findstring replacestring\n”
exit 1
fi
#echo $1 $2 $3
for i in `find . -name “$1? -exec grep -l “$2? {} \;`
do
mv “$i” “$i.sedsave”
sed “s/$2/$3/g” “$i.sedsave” > “$i”
echo $i
#rm “$i.sedsave”
done
just tried it but when i use it the filepat arg is expanded by the shell before being passed to the script.
eg. mren *.xml find replace
will expand the args so $1 = a.xml, $2=b.xml … $27=find, $28=replace
quoting the *.xml didn’t help so i had to revise the script to take find and replace args first then iterate through the remaining args.
or was there an easier way to sort that wildcard file pattern expansion issue?
By Mark on Apr 14, 2008
Thanks.
By JC on May 17, 2008
What would be the best way to search and replace all the code between tags?
Search:
>html/htmlhtml/html<
This would also need to be done recursively.
By cg on Jun 19, 2008
I have a query
find $1/ | xargs grep -l $2 > tempf
What is this doing
By devarani.r on Jul 10, 2008
Very helpful! Thanks a ton.
By Susithra on Oct 22, 2008
Scott’s suggestion in the comments is very elegant and it works for me after adding a blank string to the -i parameter.
find ./ -type f -exec sed -i ” ‘s/string1/string2/’ {} \;
Also, be careful when copypasting code, plain apostrophes get replaced by typographic ones.
Thanks Scott!
By elzr on Nov 24, 2009
Hi,
I had a problem with the script because after the replacing all the files had lost the special permission setting. everything was set to the default permission because the old file is moved away and deleted.
I used cp instead and wrote the output back in the originally to keep the permissions
Armin
#!/bin/bash
for fl in *sh; do
cp $fl tmp.old
sed ‘s/FINDSTRING/REPLACESTRING/g’ tmp.old > $fl
done
rm -f tmp.old
By arminrk on Dec 8, 2009
My problem is the string that I need to Find/Replace has slashes and quotations in them…
e.g.
Find string:
include(“http://www.mydomain.com/site/_includes
Replace string:
include($_SERVER[‘DOCUMENT_ROOT’] .”/site/_includes
I have to include the /site/_includes portion because there is other code in the page where there is the string “http://www.mydomain.com/site/linkName” where I cannot have that replace or else links will be broken
How do I include the slashes in my find such that they are not interpreted as an end of FINDSTRING as per usual?
By Derrick on Dec 15, 2009
Okay, I got it working…because my find and replace strings both had slashes I just rewrote the script and changed:
sed “s/$2/$3/g” “$i.sedsave” > “$i”
to:
sed “s+$2+$3+g” “$i.sedsave” > “$i”
full solution below
By Derrick on Dec 15, 2009
STEP-BY-STEP INSTRUCTIONS ON CREATING THE RENAME FILE (which will work recursively..I.e. all subdirectories below where you run the script)
All commands from the command line will start with #
Step1.
Go to the top location of where you want to run the script
e.g.
#cd /var/www/vhosts/yourdomain.com/httpdocs/public_html
Step 2a. [PICO METHOD]
#pico renall
OR
Step 2b. [VI METHOD]
#vi renall
#i [for insert mode]
Step 3.
paste into the new script “renall” (right-click in Putty) the following code:
#!/bin/sh
if [ $# -lt 3 ] ; then
echo -e “Wrong number of parameters.”
echo -e “Usage:”
echo -e ” renall ‘filepat’ findstring replacestring\n”
echo -e ” e.g. renall ‘*.php’ tylerperry.com tylerperryhouseofpayne.com\n”
exit 1
fi
#echo $1 $2 $3
for i in `find . -name “$1” -exec grep -l “$2” {} \;`
do
mv “$i” “$i.sedsave”
sed “s+$2+$3+g” “$i.sedsave” > “$i”
echo $i
rm “$i.sedsave”
done
Step 4a. [PICO METHOD]
Ctrl-X then Y [enter][save and quit]
Step 4b. [VI METHOD]
{ESC key} to exit insert mode
:wq [save and quit]
Step 5. make it executable
#chmod u+x renall [makes it 744)
Step 6. Execute the script named “renall”
#./renall ‘*.php’ domainA.com domainB.com
Note: if your strings are long or if they have double quotations you may enclose them in single quotations
e.g. (my actual command is below)
./renall ‘*.php’ ‘”http://www.domain.com/site/_includes’ ‘$_SERVER[‘DOCUMENT_ROOT’].”/site/_includes’
Finally: any files changed will be root:root…you will need to change this chown
e.g chown -R webmaster:webmaster shows *
Note: if your sed has slashes, then you cannot have slashes in your search or replace string.
The sed above uses plus symbols, so you cannot have plus symbols in your search or replace string.
By Derrick on Dec 15, 2009
Thanks for the code.
But that code gave me a lot of trouble, so here is my modified version:
###############################
#!/bin/sh
if [ $# -lt 3 ] ; then
echo
echo Wrong number of parameters.
echo Try again using the followinf format:
echo “myreplace ‘filepattern’ findstring replacestring”
echo
echo example:
echo “myreplace ‘*.txt’ dog cat”
echo
exit 1
fi
echo ——-
echo ——-
echo ——-
echo ——-
echo
echo All instances of :
echo $2
echo
echo will be changed to:
echo $3
echo
echo in the following files:
find . -name “$1” -exec grep -l “$2” {} \;
echo
for i in $(find . -name “$1” -exec grep -l “$2” {} \;)
do
echo “working on file ” “$i”
cp $i $i.myreplace_saved
sed “s/$2/$3/g” $i.myreplace_saved > $i
rm $i.myreplace_saved # coment this line to save backup.
done
echo
echo All changes have been made.
echo
#########################
I’m going to work on it some more to beef it up some more.
Wayne Sallee
Wayne@WayneSallee.com
By Wayne Sallee on Jan 1, 2010
Here is a more beefy version:
#################################
#!/bin/sh
if [ $# -lt 4 ] ; then
echo
echo Wrong number of parameters.
echo Try again using the following format:
echo “myreplace ‘filepattern’ findstring replacestring” “seperationstring”
echo
echo example:
echo “myreplace ‘*.txt’ \”My dog tries to eat the neighbour’s cat.\” \”My dog tries to eat the neighbour’s cat food.\” ‘#'”
echo
exit 1
fi
echo ——-
echo ——-
echo ——-
echo ——-
echo
echo All instances of :
echo $2
echo
echo will be changed to:
echo $3
echo
echo in the following files:
find . -name “$1” -exec grep -l “$2” {} \;
echo
for i in $(find . -name “$1” -exec grep -l “$2” {} \;)
do
echo “working on file ” “$i”
cp $i $i.myreplace_saved
sed “s$4$2$4$3$4g” $i.myreplace_saved > $i
rm $i.myreplace_saved # coment this line to save backup.
done
echo
echo All changes have been made.
echo
###################################
This should now cover all types of edits or so I hope.
Wayne Sallee
Wayne@WayneSallee.com
By Wayne Sallee on Jan 1, 2010
Note that the previous script does NOT work when there are spaces in the file. I tried running the script on Windows XP using cygwin.
I modified the script to be the following:
###################################
#!/bin/sh
if [ $# -lt 4 ] ; then
echo
echo Wrong number of parameters.
echo Try again using the following format:
echo “myreplace ‘filepattern’ findstring replacestring” “seperationstring”
echo
echo example:
echo “myreplace ‘*.txt’ \”My dog tries to eat the neighbour’s cat.\” \”My dog tries to eat the neighbour’s cat food.\” ‘#'”
echo
exit 1
fi
echo All instances of :
echo $2
echo
echo will be changed to:
echo $3
echo
echo in the following files:
find . -name “$1” -exec grep -l “$2” {} \;
echo
IFS=$’\n’
for i in $(find . -name “$1” -exec grep -l “$2” {} \;)
do
echo “working on file ” “$i”
# uncomment when you confirm that it works
#cp “$i” “$i.myreplace_saved”
#sed “s$4$2$4$3$4g” “$i.myreplace_saved” > “$i”
# rm $i.myreplace_saved # coment this line to save backup.
done
echo
echo All changes have been made.
echo
###################################
By Jonathan on Jul 15, 2010
i search in your website and saw this..however what i want to do is replacing in “specific” files that can be in different directory and format. meaning i have created a list of files that i want to replace eg a.sh, b.txt, c.conf and etc etc(i saved this list in a file call LIST.txt) how can i specify grep to read LIST.txt and make changes in this few files only. all the forums i find either need to sepcify some pattern but it will replace all files that have similiar pattern. what i want is just specific files.. thanks alot!!
By yvonne on Jul 17, 2010