Unix/Linux Find & Replace in Multiple Files

February 1, 2008 – 11:00 pm by Gabe Anderson | 37 Comments

Here’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. ]
  1. 37 Responses to “Unix/Linux Find & Replace in Multiple Files”

  2. Awesome little script; thanks for sharing.

    By Indrid Cold on Apr 25, 2003

  3. 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

  4. 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

  5. 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

  6. Ah, nevermind. I got it to work. Thanks.

    By sean on Jul 18, 2003

  7. 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

  8. Thanks for sharing this … was very useful.

    By Harish Puranik on Sep 3, 2003

  9. There is a nice little utility called rpl for this task. http://software.freshmeat.net/projects/rpl/

    By Gergo on Oct 26, 2003

  10. That rpl utility was very useful…it did exactly what I needed. Thanks!

    By Michael Sewell on Nov 11, 2003

  11. Useful little script, thanks :-)

    By Mario on Nov 24, 2003

  12. Thank you, thank you, thank you!
    Just what I needed.

    By Aleda Freeman on Dec 3, 2003

  13. This is great! Thanks for the tip!

    By Grant on Jan 16, 2004

  14. Nice little script, thanks. :)

    By Nick Gushlow on Feb 11, 2004

  15. Wonderful! Thanks!

    By Mandy on Apr 21, 2004

  16. Just what I was looking for! THX!

    By Sway on Apr 21, 2004

  17. Wow! How easy. Thanks!!

    By Megan on May 4, 2004

  18. 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

  19. 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

  20. Thanks a bunch! This is exactly what I needed :) Will save me tons of repetitive, mindless work heh

    By KristyX on Nov 4, 2004

  21. 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

  22. 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

  23. Thanks.

    By JC on May 17, 2008

  24. 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

  25. I have a query
    find $1/ | xargs grep -l $2 > tempf

    What is this doing

    By devarani.r on Jul 10, 2008

  26. Very helpful! Thanks a ton.

    By Susithra on Oct 22, 2008

  27. 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

  28. 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

  29. 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

  30. 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

  31. 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

  32. 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

  33. 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

  34. 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

  35. 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

  1. 3 Trackback(s)

  2. Oct 8, 2008: austenconstable.com » Blog Archive » Recursive search and replace on the Linux command line
  3. May 30, 2010: Legacy app - Spanspek
  4. Aug 3, 2012: Find and Replace Text in Multiple Files - Linux Command Line HOWTO

Post a Comment