How can I replace a string in a file(s)?Using 'sed' to find and replaceHow do I delete a line only if it is...

Fermat's polygonal number theorem

Why did Crew Dragon switch to burst disks instead of multiple check valves?

Are there 99 percentiles, or 100 percentiles? And are they groups of numbers, or dividers or pointers to individual numbers?

How to see time in ~/.bash_history file

In the comics, have any of the Robins called their costume "target attraction" for villains?

How to plot two axis bar chart side by side

Is there a historical explanation as to why the USA people are so litigious compared to France?

Idiom for a situation or event that makes one poor or even poorer?

Can you upgrade armour from breastplate to half plate?

Disordered Cryptic Orders

What is the design rationale for having armor and magic penetration mechanics?

Satellite for internet... Safe enough?

Incorrect mmap behavior when assembly files included in the project

What does すきすき mean here?

How does Firefox know my ISP login page?

Advisor asked for my entire slide presentation so she could give the presentation at an international conference

Is there a push, in the United States, to use gender-neutral language and gender pronouns (when they are given)?

How to respond to "Why didn't you do a postdoc after your PhD?"

How to prove that invoices are really UNPAID?

Function of 令 in this sentence (cantonese)

Why does Sonny say they call Michael "Giogali"?

Slaad Chaos Phage: Weak Combat Ability?

How many records can an Apex Batch process

Extra battery in the bay of an HDD



How can I replace a string in a file(s)?


Using 'sed' to find and replaceHow do I delete a line only if it is at the specified line number and it matches the pattern?how to replace a specific pattern from all filesHow can i replace a specific string within a line inside a text fileHow to replace the last octet of a valid network address with the number 2How to edit a file from a script using a loop instead of find and replace with sed?How to change certain word in certain line in a fileHow to change a single line in multiple filesGrep global find replaceModify part of line if keyword is found using sed?How to search and replace strings matching a replacement list for multiple filesReplace multiple strings in a single passSearch and replace strings that are not substrings of other stringsReplace multiline string in filesReplace string in multiple files using find and sedBash-search and replace-Merge columns in CSV fileReplace the string as $testString to replace in shell upto certain context






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{
margin-bottom:0;
}








751

















Replacing strings in files based on certain search criteria is a very common task. How can I




  • replace string foo with bar in all files in the current directory?

  • do the same recursively for sub directories?

  • replace only if the file name matches another string?

  • replace only if the string is found in a certain context?

  • replace if the string is on a certain line number?

  • replace multiple strings with the same replacement

  • replace multiple strings with different replacements










share|improve this question
























  • 2





    This is intended to be a canonical Q&A on this subject (see this meta discussion), please feel free to edit my answer below or add your own.

    – terdon
    Feb 1 '14 at 17:08


















751

















Replacing strings in files based on certain search criteria is a very common task. How can I




  • replace string foo with bar in all files in the current directory?

  • do the same recursively for sub directories?

  • replace only if the file name matches another string?

  • replace only if the string is found in a certain context?

  • replace if the string is on a certain line number?

  • replace multiple strings with the same replacement

  • replace multiple strings with different replacements










share|improve this question
























  • 2





    This is intended to be a canonical Q&A on this subject (see this meta discussion), please feel free to edit my answer below or add your own.

    – terdon
    Feb 1 '14 at 17:08














751












751








751


423






Replacing strings in files based on certain search criteria is a very common task. How can I




  • replace string foo with bar in all files in the current directory?

  • do the same recursively for sub directories?

  • replace only if the file name matches another string?

  • replace only if the string is found in a certain context?

  • replace if the string is on a certain line number?

  • replace multiple strings with the same replacement

  • replace multiple strings with different replacements










share|improve this question
















Replacing strings in files based on certain search criteria is a very common task. How can I




  • replace string foo with bar in all files in the current directory?

  • do the same recursively for sub directories?

  • replace only if the file name matches another string?

  • replace only if the string is found in a certain context?

  • replace if the string is on a certain line number?

  • replace multiple strings with the same replacement

  • replace multiple strings with different replacements







text-processing awk sed perl






share|improve this question















share|improve this question













share|improve this question




share|improve this question



share|improve this question








edited Jan 21 '17 at 0:25









Braiam

24.9k20 gold badges83 silver badges149 bronze badges




24.9k20 gold badges83 silver badges149 bronze badges










asked Feb 1 '14 at 17:05









terdonterdon

143k35 gold badges296 silver badges473 bronze badges




143k35 gold badges296 silver badges473 bronze badges











  • 2





    This is intended to be a canonical Q&A on this subject (see this meta discussion), please feel free to edit my answer below or add your own.

    – terdon
    Feb 1 '14 at 17:08














  • 2





    This is intended to be a canonical Q&A on this subject (see this meta discussion), please feel free to edit my answer below or add your own.

    – terdon
    Feb 1 '14 at 17:08








2




2





This is intended to be a canonical Q&A on this subject (see this meta discussion), please feel free to edit my answer below or add your own.

– terdon
Feb 1 '14 at 17:08





This is intended to be a canonical Q&A on this subject (see this meta discussion), please feel free to edit my answer below or add your own.

– terdon
Feb 1 '14 at 17:08










8 Answers
8






active

oldest

votes


















1009


















1. Replacing all occurrences of one string with another in all files in the current directory:



These are for cases where you know that the directory contains only regular files and that you want to process all non-hidden files. If that is not the case, use the approaches in 2.



All sed solutions in this answer assume GNU sed. If using FreeBSD or OS/X, replace -i with -i ''. Also note that the use of the -i switch with any version of sed has certain filesystem security implications and is inadvisable in any script which you plan to distribute in any way.





  • Non recursive, files in this directory only:



    sed -i -- 's/foo/bar/g' *
    perl -i -pe 's/foo/bar/g' ./*


    (the perl one will fail for file names ending in | or space)).




  • Recursive, regular files (including hidden ones) in this and all subdirectories



    find . -type f -exec sed -i 's/foo/bar/g' {} +


    If you are using zsh:



    sed -i -- 's/foo/bar/g' **/*(D.)


    (may fail if the list is too big, see zargs to work around).



    Bash can't check directly for regular files, a loop is needed (braces avoid setting the options globally):



    ( shopt -s globstar dotglob;
    for file in **; do
    if [[ -f $file ]] && [[ -w $file ]]; then
    sed -i -- 's/foo/bar/g' "$file"
    fi
    done
    )


    The files are selected when they are actual files (-f) and they are writable (-w).




2. Replace only if the file name matches another string / has a specific extension / is of a certain type etc:





  • Non-recursive, files in this directory only:



    sed -i -- 's/foo/bar/g' *baz*    ## all files whose name contains baz
    sed -i -- 's/foo/bar/g' *.baz ## files ending in .baz



  • Recursive, regular files in this and all subdirectories



    find . -type f -name "*baz*" -exec sed -i 's/foo/bar/g' {} +


    If you are using bash (braces avoid setting the options globally):



    ( shopt -s globstar dotglob
    sed -i -- 's/foo/bar/g' **baz*
    sed -i -- 's/foo/bar/g' **.baz
    )


    If you are using zsh:



    sed -i -- 's/foo/bar/g' **/*baz*(D.)
    sed -i -- 's/foo/bar/g' **/*.baz(D.)


    The -- serves to tell sed that no more flags will be given in the command line. This is useful to protect against file names starting with -.




  • If a file is of a certain type, for example, executable (see man find for more options):



    find . -type f -executable -exec sed -i 's/foo/bar/g' {} +


    zsh:



    sed -i -- 's/foo/bar/g' **/*(D*)



3. Replace only if the string is found in a certain context





  • Replace foo with bar only if there is a baz later on the same line:



    sed -i 's/foo(.*baz)/bar1/' file


    In sed, using ( ) saves whatever is in the parentheses and you can then access it with 1. There are many variations of this theme, to learn more about such regular expressions, see here.




  • Replace foo with bar only if foo is found on the 3d column (field) of the input file (assuming whitespace-separated fields):



    gawk -i inplace '{gsub(/foo/,"baz",$3); print}' file


    (needs gawk 4.1.0 or newer).




  • For a different field just use $N where N is the number of the field of interest. For a different field separator (: in this example) use:



    gawk -i inplace -F':' '{gsub(/foo/,"baz",$3);print}' file


    Another solution using perl:



    perl -i -ane '$F[2]=~s/foo/baz/g; $" = " "; print "@Fn"' foo 


    NOTE: both the awk and perl solutions will affect spacing in the file (remove the leading and trailing blanks, and convert sequences of blanks to one space character in those lines that match). For a different field, use $F[N-1] where N is the field number you want and for a different field separator use (the $"=":" sets the output field separator to :):



    perl -i -F':' -ane '$F[2]=~s/foo/baz/g; $"=":";print "@F"' foo 



  • Replace foo with bar only on the 4th line:



    sed -i '4s/foo/bar/g' file
    gawk -i inplace 'NR==4{gsub(/foo/,"baz")};1' file
    perl -i -pe 's/foo/bar/g if $.==4' file



4. Multiple replace operations: replace with different strings





  • You can combine sed commands:



    sed -i 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file


    Be aware that order matters (sed 's/foo/bar/g; s/bar/baz/g' will substitute foo with baz).




  • or Perl commands



    perl -i -pe 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file



  • If you have a large number of patterns, it is easier to save your patterns and their replacements in a sed script file:



    #! /usr/bin/sed -f
    s/foo/bar/g
    s/baz/zab/g



  • Or, if you have too many pattern pairs for the above to be feasible, you can read pattern pairs from a file (two space separated patterns, $pattern and $replacement, per line):



    while read -r pattern replacement; do   
    sed -i "s/$pattern/$replacement/" file
    done < patterns.txt



  • That will be quite slow for long lists of patterns and large data files so you might want to read the patterns and create a sed script from them instead. The following assumes a <space> delimiter separates a list of MATCH<space>REPLACE pairs occurring one-per-line in the file patterns.txt :



    sed 's| *([^ ]*) *([^ ]*).*|s/1/2/g|' <patterns.txt |
    sed -f- ./editfile >outfile


    The above format is largely arbitrary and, for example, doesn't allow for a <space> in either of MATCH or REPLACE. The method is very general though: basically, if you can create an output stream which looks like a sed script, then you can source that stream as a sed script by specifying sed's script file as -stdin.




  • You can combine and concatenate multiple scripts in similar fashion:



    SOME_PIPELINE |
    sed -e'#some expression script'
    -f./script_file -f-
    -e'#more inline expressions'
    ./actual_edit_file >./outfile


    A POSIX sed will concatenate all scripts into one in the order they appear on the command-line. None of these need end in a newline.




  • grep can work the same way:



    sed -e'#generate a pattern list' <in |
    grep -f- ./grepped_file



  • When working with fixed-strings as patterns, it is good practice to escape regular expression metacharacters. You can do this rather easily:



    sed 's/[]$&^*./[]/\&/g
    s| *([^ ]*) *([^ ]*).*|s/1/2/g|
    ' <patterns.txt |
    sed -f- ./editfile >outfile



5. Multiple replace operations: replace multiple patterns with the same string





  • Replace any of foo, bar or baz with foobar



    sed -Ei 's/foo|bar|baz/foobar/g' file



  • or



    perl -i -pe 's/foo|bar|baz/foobar/g' file







share|improve this answer
























  • 2





    @StéphaneChazelas thanks for the edit, it did indeed fix several things. However, please don't remove information that is relevant to bash. Not everyone uses zsh. By all means add zsh info but there is no reason to remove the bash stuff. Also, I know that using the shell for text processing is not ideal but there are cases where it is needed. I edited in a better version of my original script that will create a sed script instead of actually using the shell loop to parse. This can be useful if you have several hundred pairs of patterns for example.

    – terdon
    Jan 16 '15 at 15:10






  • 2





    @terdon, your bash one is incorrect. bash before 4.3 will follow symlinks when descending. Also bash has no equivalent for the (.) globbing qualifier so can't be used here. (you're missing some -- as well). The for loop is incorrect (missing -r) and means making several passes in the files and adds no benefit over a sed script.

    – Stéphane Chazelas
    Jan 16 '15 at 15:16






  • 7





    @terdon What does -- after sed -i and before the substitute command indicate?

    – Geek
    Sep 28 '15 at 11:29








  • 5





    @Geek that's a POSIX thing. It signifies the end of options and lets you pass arguments starting with -. Using it ensures that the commands will work on files with names like -foo. Without it, the -f would be parsed as an option.

    – terdon
    Sep 28 '15 at 11:42








  • 1





    Be very careful executing some of the recursive commands in git repositories. For example, the solutions provided in section 1 of this answer will actually modify internal git files in a .git directory, and actually mess up your checkout. Better to operate within/on specific directories by name.

    – Pistos
    Apr 19 '16 at 14:44



















75


















A good replacement Linux tool is rpl, that was originally written for the Debian project, so it is available with apt-get install rpl in any Debian derived distro, and may be for others, but otherwise you can download the tar.gz file in SourgeForge.



Simplest example of use:



 $ rpl old_string new_string test.txt


Note that if the string contain spaces it should be enclosed in quotation marks. By default rpl take care of capital letters but not of complete words, but you can change these defaults with options -i (ignore case) and -w (whole words). You can also specify multiple files:



 $ rpl -i -w "old string" "new string" test.txt test2.txt


Or even specify the extensions (-x) to search or even search recursively (-R) in the directory:



 $ rpl -x .html -x .txt -R old_string new_string test*


You can also search/replace in interactive mode with -p (prompt) option:



The output show the numbers of files/string replaced and the type of search (case in/sensitive, whole/partial words), but it can be silent with the -q (quiet mode) option, or even more verbose, listing line numbers that contain matches of each file and directory with -v (verbose mode) option.



Other options that are worth remembering are -e (honor escapes) that allow regular expressions, so you can search also tabs (t), new lines (n),etc. Even you can use -f to force permissions (of course, only when the user have write permissions) and -d to preserve the modification times`).



Finally, if you are unsure of which will make exactly, use the -s (simulate mode).






share|improve this answer
























  • 2





    So much better at the feedback and simplicity than sed. I just wish it allowed acting on file names, and then it'd be perfect as-is.

    – Kzqai
    Dec 23 '16 at 17:12






  • 1





    i like the -s (simulate mode) :-)

    – erm3nda
    Jun 10 '18 at 11:08



















25


















How to do a search and replace over multiple files suggests:




You could also use find and sed, but I find that this little line of
perl works nicely.



perl -pi -w -e 's/search/replace/g;' *.php



  • -e means execute the following line of code.

  • -i means edit in-place

  • -w write warnings

  • -p loop over the input file, printing each line after the script is applied to it.




My best results come from using perl and grep (to ensure that file have the search expression )



perl -pi -w -e 's/search/replace/g;' $( grep -rl 'search' )





share|improve this answer




































    13


















    You can use Vim in Ex mode:




    replace string ALF with BRA in all files in the current directory?




    for CHA in *
    do
    ex -sc '%s/ALF/BRA/g' -cx "$CHA"
    done





    do the same recursively for sub directories?




    find -type f -exec ex -sc '%s/ALF/BRA/g' -cx {} ';'





    replace only if the file name matches another string?




    for CHA in *.txt
    do
    ex -sc '%s/ALF/BRA/g' -cx "$CHA"
    done





    replace only if the string is found in a certain context?




    ex -sc 'g/DEL/s/ALF/BRA/g' -cx file





    replace if the string is on a certain line number?




    ex -sc '2s/ALF/BRA/g' -cx file





    replace multiple strings with the same replacement




    ex -sc '%s/vALF|ECH/BRA/g' -cx file





    replace multiple strings with different replacements




    ex -sc '%s/ALF/BRA/g|%s/FOX/GOL/g' -cx file





    share|improve this answer




































      13


















      I used this:



      grep -r "old_string" -l | tr 'n' ' ' | xargs sed -i 's/old_string/new_string/g'



      1. List all files that contain old_string.


      2. Replace newline in result with spaces (so that the list of files can be fed to sed.


      3. Run sed on those files to replace old string with new.



      Update: The above result will fail on filenames that contain whitespaces. Instead, use:



      grep --null -lr "old_string" | xargs --null sed -i 's/old_string/new_string/g'






      share|improve this answer





























      • Note that this will fail if any of your file names contain spaces, tabs or newlines. Use grep --null -lr "old_string" | xargs --null sed -i 's/old_string/new_string/g' will make it deal with arbitrary file names.

        – terdon
        Oct 26 '15 at 17:07













      • thanks guys. added update and left the old code cause it's an interesting caveat that could be useful to someone unaware of this behavior.

        – o_o_o--
        Oct 26 '15 at 20:59





















      6


















      From a user's perspective, a nice & simple Unix tool that does the job perfectly is qsubst. For example,



      % qsubst foo bar *.c *.h


      will replace foo with bar in all my C files. A nice feature is that qsubst will do a query-replace, i.e., it will show me each occurrence of foo and ask whether I want to replace it or not. [You can replace unconditionally (no asking) with -go option, and there are other options, e.g., -w if you only want to replace foo when it is a whole word.]



      How to get it: qsubst was invented by der Mouse (from McGill) and posted to comp.unix.sources 11(7) in Aug. 1987. Updated versions exist. For example, the NetBSD version qsubst.c,v 1.8 2004/11/01 compiles and runs perfectly on my mac.






      share|improve this answer




































        2


















        I needed something that would provide a dry-run option and would work recursively with a glob, and after trying to do it with awk and sed I gave up and instead did it in python.



        The script searches recursively all files matching a glob pattern (e.g. --glob="*.html") for a regex and replaces with the replacement regex:



        find_replace.py [--dir=my_folder] 
        --search-regex=<search_regex>
        --replace-regex=<replace_regex>
        --glob=[glob_pattern]
        --dry-run


        Every long option such as --search-regex has a corresponding short option, i.e. -s. Run with -h to see all options.



        For example, this will flip all dates from 2017-12-31 to 31-12-2017:



        python replace.py --glob=myfile.txt 
        --search-regex="(d{4})-(d{2})-(d{2})"
        --replace-regex="3-2-1"
        --dry-run --verbose


        import os
        import fnmatch
        import sys
        import shutil
        import re

        import argparse

        def find_replace(cfg):
        search_pattern = re.compile(cfg.search_regex)

        if cfg.dry_run:
        print('THIS IS A DRY RUN -- NO FILES WILL BE CHANGED!')

        for path, dirs, files in os.walk(os.path.abspath(cfg.dir)):
        for filename in fnmatch.filter(files, cfg.glob):

        if cfg.print_parent_folder:
        pardir = os.path.normpath(os.path.join(path, '..'))
        pardir = os.path.split(pardir)[-1]
        print('[%s]' % pardir)
        filepath = os.path.join(path, filename)

        # backup original file
        if cfg.create_backup:
        backup_path = filepath + '.bak'

        while os.path.exists(backup_path):
        backup_path += '.bak'
        print('DBG: creating backup', backup_path)
        shutil.copyfile(filepath, backup_path)

        with open(filepath) as f:
        old_text = f.read()

        all_matches = search_pattern.findall(old_text)

        if all_matches:

        print('Found {} matches in file {}'.format(len(all_matches), filename))

        new_text = search_pattern.sub(cfg.replace_regex, old_text)

        if not cfg.dry_run:
        with open(filepath, "w") as f:
        print('DBG: replacing in file', filepath)
        f.write(new_text)
        else:
        for idx, matches in enumerate(all_matches):
        print("Match #{}: {}".format(idx, matches))

        print("NEW TEXT:n{}".format(new_text))

        elif cfg.verbose:
        print('File {} does not contain search regex "{}"'.format(filename, cfg.search_regex))


        if __name__ == '__main__':

        parser = argparse.ArgumentParser(description='''DESCRIPTION:
        Find and replace recursively from the given folder using regular expressions''',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog='''USAGE:
        {0} -d [my_folder] -s <search_regex> -r <replace_regex> -g [glob_pattern]

        '''.format(os.path.basename(sys.argv[0])))

        parser.add_argument('--dir', '-d',
        help='folder to search in; by default current folder',
        default='.')

        parser.add_argument('--search-regex', '-s',
        help='search regex',
        required=True)

        parser.add_argument('--replace-regex', '-r',
        help='replacement regex',
        required=True)

        parser.add_argument('--glob', '-g',
        help='glob pattern, i.e. *.html',
        default="*.*")

        parser.add_argument('--dry-run', '-dr',
        action='store_true',
        help="don't replace anything just show what is going to be done",
        default=False)

        parser.add_argument('--create-backup', '-b',
        action='store_true',
        help='Create backup files',
        default=False)

        parser.add_argument('--verbose', '-v',
        action='store_true',
        help="Show files which don't match the search regex",
        default=False)

        parser.add_argument('--print-parent-folder', '-p',
        action='store_true',
        help="Show the parent info for debug",
        default=False)

        config = parser.parse_args(sys.argv[1:])

        find_replace(config)



        Here is an updated version of the script which highlights the search terms and replacements with different colors.







        share|improve this answer
























        • 1





          I don't understand why you would make something this complex. For recursion, use either bash's (or your shell's equivalent) globstar option and ** globs or find. For a dry run, just use sed. Unless you use the -i option, it won't make any changes. For a backup use sed -i.bak (or perl -i .bak); for files that don't match, use grep PATTERN file || echo file. And why in the world would you have python expand the glob instead of letting the shell do it? Why script.py --glob=foo* instead of just script.py foo*?

          – terdon
          Nov 23 '17 at 9:34








        • 1





          My why's are very simple: (1) above all, ease of debugging; (2) using only a single well documented tool with a supportive community (3) not knowing sed and awk well and being unwilling to invest extra time on mastering them, (4) readability, (5) this solution will also work on non-posix systems (not that I need that but somebody else might).

          – ccpizza
          Nov 23 '17 at 12:59





















        0


















        ripgrep (command name rg) is a grep tool, but supports search and replace as well.



        $ cat ip.txt
        dark blue and light blue
        light orange
        blue sky
        $ # by default, line number is displayed if output destination is stdout
        $ # by default, only lines that matched the given pattern is displayed
        $ # 'blue' is search pattern and -r 'red' is replacement string
        $ rg 'blue' -r 'red' ip.txt
        1:dark red and light red
        3:red sky

        $ # --passthru option is useful to print all lines, whether or not it matched
        $ # -N will disable line number prefix
        $ # this command is similar to: sed 's/blue/red/g' ip.txt
        $ rg --passthru -N 'blue' -r 'red' ip.txt
        dark red and light red
        light orange
        red sky



        rg doesn't support in-place option, so you'll have to do it yourself



        $ # -N isn't needed here as output destination is a file
        $ rg --passthru 'blue' -r 'red' ip.txt > tmp.txt && mv tmp.txt ip.txt
        $ cat ip.txt
        dark red and light red
        light orange
        red sky




        See Rust regex documentation for regular expression syntax and features. The -P switch will enable PCRE2 flavor. rg supports Unicode by default.



        $ # non-greedy quantifier is supported
        $ echo 'food land bark sand band cue combat' | rg 'foo.*?ba' -r 'X'
        Xrk sand band cue combat

        $ # unicode support
        $ echo 'fox:αλεπού,eagle:αετός' | rg 'p{L}+' -r '($0)'
        (fox):(αλεπού),(eagle):(αετός)

        $ # set operator example, remove all punctuation characters except . ! and ?
        $ para='"Hi", there! How *are* you? All fine here.'
        $ echo "$para" | rg '[[:punct:]--[.!?]]+' -r ''
        Hi there! How are you? All fine here.

        $ # use -P if you need even more advanced features
        $ echo 'car bat cod map' | rg -P '(bat|map)(*SKIP)(*F)|w+' -r '[$0]'
        [car] bat [cod] map




        Like grep, the -F option will allow fixed strings to be matched, a handy option which I feel sed should implement too.



        $ printf '2.3/[4]*6nfoon5.3-[4]*9n' | rg --passthru -F '[4]*' -r '2'
        2.3/26
        foo
        5.3-29




        Another handy option is -U which enables multiline matching



        $ # (?s) flag will allow . to match newline characters as well
        $ printf '42nHi therenHave a Nice Day' | rg --passthru -U '(?s)the.*ice' -r ''
        42
        Hi Day



        rg can handle dos-style files too



        $ # same as: sed -E 's/w+(r?)$/1231/'
        $ printf 'hi thererngood dayrn' | rg --passthru --crlf 'w+$' -r '123'
        hi 123
        good 123




        Another advantage of rg is that it is likely to be faster than sed



        $ # for small files, initial processing time of rg is a large component
        $ time echo 'aba' | sed 's/a/b/g' > f1
        real 0m0.002s
        $ time echo 'aba' | rg --passthru 'a' -r 'b' > f2
        real 0m0.007s

        $ # for larger files, rg is likely to be faster
        $ # 6.2M sample ASCII file
        $ wget https://norvig.com/big.txt
        $ time LC_ALL=C sed 's/bcatb/dog/g' big.txt > f1
        real 0m0.060s
        $ time rg --passthru 'bcatb' -r 'dog' big.txt > f2
        real 0m0.048s
        $ diff -s f1 f2
        Files f1 and f2 are identical

        $ time LC_ALL=C sed -E 's/b(w+)(s+1)+b/1/g' big.txt > f1
        real 0m0.725s
        $ time rg --no-pcre2-unicode --passthru -wP '(w+)(s+1)+' -r '$1' big.txt > f2
        real 0m0.093s
        $ diff -s f1 f2
        Files f1 and f2 are identical





        share|improve this answer


























          protected by Community Jul 27 '15 at 21:05



          Thank you for your interest in this question.
          Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).



          Would you like to answer one of these unanswered questions instead?














          8 Answers
          8






          active

          oldest

          votes








          8 Answers
          8






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          1009


















          1. Replacing all occurrences of one string with another in all files in the current directory:



          These are for cases where you know that the directory contains only regular files and that you want to process all non-hidden files. If that is not the case, use the approaches in 2.



          All sed solutions in this answer assume GNU sed. If using FreeBSD or OS/X, replace -i with -i ''. Also note that the use of the -i switch with any version of sed has certain filesystem security implications and is inadvisable in any script which you plan to distribute in any way.





          • Non recursive, files in this directory only:



            sed -i -- 's/foo/bar/g' *
            perl -i -pe 's/foo/bar/g' ./*


            (the perl one will fail for file names ending in | or space)).




          • Recursive, regular files (including hidden ones) in this and all subdirectories



            find . -type f -exec sed -i 's/foo/bar/g' {} +


            If you are using zsh:



            sed -i -- 's/foo/bar/g' **/*(D.)


            (may fail if the list is too big, see zargs to work around).



            Bash can't check directly for regular files, a loop is needed (braces avoid setting the options globally):



            ( shopt -s globstar dotglob;
            for file in **; do
            if [[ -f $file ]] && [[ -w $file ]]; then
            sed -i -- 's/foo/bar/g' "$file"
            fi
            done
            )


            The files are selected when they are actual files (-f) and they are writable (-w).




          2. Replace only if the file name matches another string / has a specific extension / is of a certain type etc:





          • Non-recursive, files in this directory only:



            sed -i -- 's/foo/bar/g' *baz*    ## all files whose name contains baz
            sed -i -- 's/foo/bar/g' *.baz ## files ending in .baz



          • Recursive, regular files in this and all subdirectories



            find . -type f -name "*baz*" -exec sed -i 's/foo/bar/g' {} +


            If you are using bash (braces avoid setting the options globally):



            ( shopt -s globstar dotglob
            sed -i -- 's/foo/bar/g' **baz*
            sed -i -- 's/foo/bar/g' **.baz
            )


            If you are using zsh:



            sed -i -- 's/foo/bar/g' **/*baz*(D.)
            sed -i -- 's/foo/bar/g' **/*.baz(D.)


            The -- serves to tell sed that no more flags will be given in the command line. This is useful to protect against file names starting with -.




          • If a file is of a certain type, for example, executable (see man find for more options):



            find . -type f -executable -exec sed -i 's/foo/bar/g' {} +


            zsh:



            sed -i -- 's/foo/bar/g' **/*(D*)



          3. Replace only if the string is found in a certain context





          • Replace foo with bar only if there is a baz later on the same line:



            sed -i 's/foo(.*baz)/bar1/' file


            In sed, using ( ) saves whatever is in the parentheses and you can then access it with 1. There are many variations of this theme, to learn more about such regular expressions, see here.




          • Replace foo with bar only if foo is found on the 3d column (field) of the input file (assuming whitespace-separated fields):



            gawk -i inplace '{gsub(/foo/,"baz",$3); print}' file


            (needs gawk 4.1.0 or newer).




          • For a different field just use $N where N is the number of the field of interest. For a different field separator (: in this example) use:



            gawk -i inplace -F':' '{gsub(/foo/,"baz",$3);print}' file


            Another solution using perl:



            perl -i -ane '$F[2]=~s/foo/baz/g; $" = " "; print "@Fn"' foo 


            NOTE: both the awk and perl solutions will affect spacing in the file (remove the leading and trailing blanks, and convert sequences of blanks to one space character in those lines that match). For a different field, use $F[N-1] where N is the field number you want and for a different field separator use (the $"=":" sets the output field separator to :):



            perl -i -F':' -ane '$F[2]=~s/foo/baz/g; $"=":";print "@F"' foo 



          • Replace foo with bar only on the 4th line:



            sed -i '4s/foo/bar/g' file
            gawk -i inplace 'NR==4{gsub(/foo/,"baz")};1' file
            perl -i -pe 's/foo/bar/g if $.==4' file



          4. Multiple replace operations: replace with different strings





          • You can combine sed commands:



            sed -i 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file


            Be aware that order matters (sed 's/foo/bar/g; s/bar/baz/g' will substitute foo with baz).




          • or Perl commands



            perl -i -pe 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file



          • If you have a large number of patterns, it is easier to save your patterns and their replacements in a sed script file:



            #! /usr/bin/sed -f
            s/foo/bar/g
            s/baz/zab/g



          • Or, if you have too many pattern pairs for the above to be feasible, you can read pattern pairs from a file (two space separated patterns, $pattern and $replacement, per line):



            while read -r pattern replacement; do   
            sed -i "s/$pattern/$replacement/" file
            done < patterns.txt



          • That will be quite slow for long lists of patterns and large data files so you might want to read the patterns and create a sed script from them instead. The following assumes a <space> delimiter separates a list of MATCH<space>REPLACE pairs occurring one-per-line in the file patterns.txt :



            sed 's| *([^ ]*) *([^ ]*).*|s/1/2/g|' <patterns.txt |
            sed -f- ./editfile >outfile


            The above format is largely arbitrary and, for example, doesn't allow for a <space> in either of MATCH or REPLACE. The method is very general though: basically, if you can create an output stream which looks like a sed script, then you can source that stream as a sed script by specifying sed's script file as -stdin.




          • You can combine and concatenate multiple scripts in similar fashion:



            SOME_PIPELINE |
            sed -e'#some expression script'
            -f./script_file -f-
            -e'#more inline expressions'
            ./actual_edit_file >./outfile


            A POSIX sed will concatenate all scripts into one in the order they appear on the command-line. None of these need end in a newline.




          • grep can work the same way:



            sed -e'#generate a pattern list' <in |
            grep -f- ./grepped_file



          • When working with fixed-strings as patterns, it is good practice to escape regular expression metacharacters. You can do this rather easily:



            sed 's/[]$&^*./[]/\&/g
            s| *([^ ]*) *([^ ]*).*|s/1/2/g|
            ' <patterns.txt |
            sed -f- ./editfile >outfile



          5. Multiple replace operations: replace multiple patterns with the same string





          • Replace any of foo, bar or baz with foobar



            sed -Ei 's/foo|bar|baz/foobar/g' file



          • or



            perl -i -pe 's/foo|bar|baz/foobar/g' file







          share|improve this answer
























          • 2





            @StéphaneChazelas thanks for the edit, it did indeed fix several things. However, please don't remove information that is relevant to bash. Not everyone uses zsh. By all means add zsh info but there is no reason to remove the bash stuff. Also, I know that using the shell for text processing is not ideal but there are cases where it is needed. I edited in a better version of my original script that will create a sed script instead of actually using the shell loop to parse. This can be useful if you have several hundred pairs of patterns for example.

            – terdon
            Jan 16 '15 at 15:10






          • 2





            @terdon, your bash one is incorrect. bash before 4.3 will follow symlinks when descending. Also bash has no equivalent for the (.) globbing qualifier so can't be used here. (you're missing some -- as well). The for loop is incorrect (missing -r) and means making several passes in the files and adds no benefit over a sed script.

            – Stéphane Chazelas
            Jan 16 '15 at 15:16






          • 7





            @terdon What does -- after sed -i and before the substitute command indicate?

            – Geek
            Sep 28 '15 at 11:29








          • 5





            @Geek that's a POSIX thing. It signifies the end of options and lets you pass arguments starting with -. Using it ensures that the commands will work on files with names like -foo. Without it, the -f would be parsed as an option.

            – terdon
            Sep 28 '15 at 11:42








          • 1





            Be very careful executing some of the recursive commands in git repositories. For example, the solutions provided in section 1 of this answer will actually modify internal git files in a .git directory, and actually mess up your checkout. Better to operate within/on specific directories by name.

            – Pistos
            Apr 19 '16 at 14:44
















          1009


















          1. Replacing all occurrences of one string with another in all files in the current directory:



          These are for cases where you know that the directory contains only regular files and that you want to process all non-hidden files. If that is not the case, use the approaches in 2.



          All sed solutions in this answer assume GNU sed. If using FreeBSD or OS/X, replace -i with -i ''. Also note that the use of the -i switch with any version of sed has certain filesystem security implications and is inadvisable in any script which you plan to distribute in any way.





          • Non recursive, files in this directory only:



            sed -i -- 's/foo/bar/g' *
            perl -i -pe 's/foo/bar/g' ./*


            (the perl one will fail for file names ending in | or space)).




          • Recursive, regular files (including hidden ones) in this and all subdirectories



            find . -type f -exec sed -i 's/foo/bar/g' {} +


            If you are using zsh:



            sed -i -- 's/foo/bar/g' **/*(D.)


            (may fail if the list is too big, see zargs to work around).



            Bash can't check directly for regular files, a loop is needed (braces avoid setting the options globally):



            ( shopt -s globstar dotglob;
            for file in **; do
            if [[ -f $file ]] && [[ -w $file ]]; then
            sed -i -- 's/foo/bar/g' "$file"
            fi
            done
            )


            The files are selected when they are actual files (-f) and they are writable (-w).




          2. Replace only if the file name matches another string / has a specific extension / is of a certain type etc:





          • Non-recursive, files in this directory only:



            sed -i -- 's/foo/bar/g' *baz*    ## all files whose name contains baz
            sed -i -- 's/foo/bar/g' *.baz ## files ending in .baz



          • Recursive, regular files in this and all subdirectories



            find . -type f -name "*baz*" -exec sed -i 's/foo/bar/g' {} +


            If you are using bash (braces avoid setting the options globally):



            ( shopt -s globstar dotglob
            sed -i -- 's/foo/bar/g' **baz*
            sed -i -- 's/foo/bar/g' **.baz
            )


            If you are using zsh:



            sed -i -- 's/foo/bar/g' **/*baz*(D.)
            sed -i -- 's/foo/bar/g' **/*.baz(D.)


            The -- serves to tell sed that no more flags will be given in the command line. This is useful to protect against file names starting with -.




          • If a file is of a certain type, for example, executable (see man find for more options):



            find . -type f -executable -exec sed -i 's/foo/bar/g' {} +


            zsh:



            sed -i -- 's/foo/bar/g' **/*(D*)



          3. Replace only if the string is found in a certain context





          • Replace foo with bar only if there is a baz later on the same line:



            sed -i 's/foo(.*baz)/bar1/' file


            In sed, using ( ) saves whatever is in the parentheses and you can then access it with 1. There are many variations of this theme, to learn more about such regular expressions, see here.




          • Replace foo with bar only if foo is found on the 3d column (field) of the input file (assuming whitespace-separated fields):



            gawk -i inplace '{gsub(/foo/,"baz",$3); print}' file


            (needs gawk 4.1.0 or newer).




          • For a different field just use $N where N is the number of the field of interest. For a different field separator (: in this example) use:



            gawk -i inplace -F':' '{gsub(/foo/,"baz",$3);print}' file


            Another solution using perl:



            perl -i -ane '$F[2]=~s/foo/baz/g; $" = " "; print "@Fn"' foo 


            NOTE: both the awk and perl solutions will affect spacing in the file (remove the leading and trailing blanks, and convert sequences of blanks to one space character in those lines that match). For a different field, use $F[N-1] where N is the field number you want and for a different field separator use (the $"=":" sets the output field separator to :):



            perl -i -F':' -ane '$F[2]=~s/foo/baz/g; $"=":";print "@F"' foo 



          • Replace foo with bar only on the 4th line:



            sed -i '4s/foo/bar/g' file
            gawk -i inplace 'NR==4{gsub(/foo/,"baz")};1' file
            perl -i -pe 's/foo/bar/g if $.==4' file



          4. Multiple replace operations: replace with different strings





          • You can combine sed commands:



            sed -i 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file


            Be aware that order matters (sed 's/foo/bar/g; s/bar/baz/g' will substitute foo with baz).




          • or Perl commands



            perl -i -pe 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file



          • If you have a large number of patterns, it is easier to save your patterns and their replacements in a sed script file:



            #! /usr/bin/sed -f
            s/foo/bar/g
            s/baz/zab/g



          • Or, if you have too many pattern pairs for the above to be feasible, you can read pattern pairs from a file (two space separated patterns, $pattern and $replacement, per line):



            while read -r pattern replacement; do   
            sed -i "s/$pattern/$replacement/" file
            done < patterns.txt



          • That will be quite slow for long lists of patterns and large data files so you might want to read the patterns and create a sed script from them instead. The following assumes a <space> delimiter separates a list of MATCH<space>REPLACE pairs occurring one-per-line in the file patterns.txt :



            sed 's| *([^ ]*) *([^ ]*).*|s/1/2/g|' <patterns.txt |
            sed -f- ./editfile >outfile


            The above format is largely arbitrary and, for example, doesn't allow for a <space> in either of MATCH or REPLACE. The method is very general though: basically, if you can create an output stream which looks like a sed script, then you can source that stream as a sed script by specifying sed's script file as -stdin.




          • You can combine and concatenate multiple scripts in similar fashion:



            SOME_PIPELINE |
            sed -e'#some expression script'
            -f./script_file -f-
            -e'#more inline expressions'
            ./actual_edit_file >./outfile


            A POSIX sed will concatenate all scripts into one in the order they appear on the command-line. None of these need end in a newline.




          • grep can work the same way:



            sed -e'#generate a pattern list' <in |
            grep -f- ./grepped_file



          • When working with fixed-strings as patterns, it is good practice to escape regular expression metacharacters. You can do this rather easily:



            sed 's/[]$&^*./[]/\&/g
            s| *([^ ]*) *([^ ]*).*|s/1/2/g|
            ' <patterns.txt |
            sed -f- ./editfile >outfile



          5. Multiple replace operations: replace multiple patterns with the same string





          • Replace any of foo, bar or baz with foobar



            sed -Ei 's/foo|bar|baz/foobar/g' file



          • or



            perl -i -pe 's/foo|bar|baz/foobar/g' file







          share|improve this answer
























          • 2





            @StéphaneChazelas thanks for the edit, it did indeed fix several things. However, please don't remove information that is relevant to bash. Not everyone uses zsh. By all means add zsh info but there is no reason to remove the bash stuff. Also, I know that using the shell for text processing is not ideal but there are cases where it is needed. I edited in a better version of my original script that will create a sed script instead of actually using the shell loop to parse. This can be useful if you have several hundred pairs of patterns for example.

            – terdon
            Jan 16 '15 at 15:10






          • 2





            @terdon, your bash one is incorrect. bash before 4.3 will follow symlinks when descending. Also bash has no equivalent for the (.) globbing qualifier so can't be used here. (you're missing some -- as well). The for loop is incorrect (missing -r) and means making several passes in the files and adds no benefit over a sed script.

            – Stéphane Chazelas
            Jan 16 '15 at 15:16






          • 7





            @terdon What does -- after sed -i and before the substitute command indicate?

            – Geek
            Sep 28 '15 at 11:29








          • 5





            @Geek that's a POSIX thing. It signifies the end of options and lets you pass arguments starting with -. Using it ensures that the commands will work on files with names like -foo. Without it, the -f would be parsed as an option.

            – terdon
            Sep 28 '15 at 11:42








          • 1





            Be very careful executing some of the recursive commands in git repositories. For example, the solutions provided in section 1 of this answer will actually modify internal git files in a .git directory, and actually mess up your checkout. Better to operate within/on specific directories by name.

            – Pistos
            Apr 19 '16 at 14:44














          1009














          1009










          1009









          1. Replacing all occurrences of one string with another in all files in the current directory:



          These are for cases where you know that the directory contains only regular files and that you want to process all non-hidden files. If that is not the case, use the approaches in 2.



          All sed solutions in this answer assume GNU sed. If using FreeBSD or OS/X, replace -i with -i ''. Also note that the use of the -i switch with any version of sed has certain filesystem security implications and is inadvisable in any script which you plan to distribute in any way.





          • Non recursive, files in this directory only:



            sed -i -- 's/foo/bar/g' *
            perl -i -pe 's/foo/bar/g' ./*


            (the perl one will fail for file names ending in | or space)).




          • Recursive, regular files (including hidden ones) in this and all subdirectories



            find . -type f -exec sed -i 's/foo/bar/g' {} +


            If you are using zsh:



            sed -i -- 's/foo/bar/g' **/*(D.)


            (may fail if the list is too big, see zargs to work around).



            Bash can't check directly for regular files, a loop is needed (braces avoid setting the options globally):



            ( shopt -s globstar dotglob;
            for file in **; do
            if [[ -f $file ]] && [[ -w $file ]]; then
            sed -i -- 's/foo/bar/g' "$file"
            fi
            done
            )


            The files are selected when they are actual files (-f) and they are writable (-w).




          2. Replace only if the file name matches another string / has a specific extension / is of a certain type etc:





          • Non-recursive, files in this directory only:



            sed -i -- 's/foo/bar/g' *baz*    ## all files whose name contains baz
            sed -i -- 's/foo/bar/g' *.baz ## files ending in .baz



          • Recursive, regular files in this and all subdirectories



            find . -type f -name "*baz*" -exec sed -i 's/foo/bar/g' {} +


            If you are using bash (braces avoid setting the options globally):



            ( shopt -s globstar dotglob
            sed -i -- 's/foo/bar/g' **baz*
            sed -i -- 's/foo/bar/g' **.baz
            )


            If you are using zsh:



            sed -i -- 's/foo/bar/g' **/*baz*(D.)
            sed -i -- 's/foo/bar/g' **/*.baz(D.)


            The -- serves to tell sed that no more flags will be given in the command line. This is useful to protect against file names starting with -.




          • If a file is of a certain type, for example, executable (see man find for more options):



            find . -type f -executable -exec sed -i 's/foo/bar/g' {} +


            zsh:



            sed -i -- 's/foo/bar/g' **/*(D*)



          3. Replace only if the string is found in a certain context





          • Replace foo with bar only if there is a baz later on the same line:



            sed -i 's/foo(.*baz)/bar1/' file


            In sed, using ( ) saves whatever is in the parentheses and you can then access it with 1. There are many variations of this theme, to learn more about such regular expressions, see here.




          • Replace foo with bar only if foo is found on the 3d column (field) of the input file (assuming whitespace-separated fields):



            gawk -i inplace '{gsub(/foo/,"baz",$3); print}' file


            (needs gawk 4.1.0 or newer).




          • For a different field just use $N where N is the number of the field of interest. For a different field separator (: in this example) use:



            gawk -i inplace -F':' '{gsub(/foo/,"baz",$3);print}' file


            Another solution using perl:



            perl -i -ane '$F[2]=~s/foo/baz/g; $" = " "; print "@Fn"' foo 


            NOTE: both the awk and perl solutions will affect spacing in the file (remove the leading and trailing blanks, and convert sequences of blanks to one space character in those lines that match). For a different field, use $F[N-1] where N is the field number you want and for a different field separator use (the $"=":" sets the output field separator to :):



            perl -i -F':' -ane '$F[2]=~s/foo/baz/g; $"=":";print "@F"' foo 



          • Replace foo with bar only on the 4th line:



            sed -i '4s/foo/bar/g' file
            gawk -i inplace 'NR==4{gsub(/foo/,"baz")};1' file
            perl -i -pe 's/foo/bar/g if $.==4' file



          4. Multiple replace operations: replace with different strings





          • You can combine sed commands:



            sed -i 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file


            Be aware that order matters (sed 's/foo/bar/g; s/bar/baz/g' will substitute foo with baz).




          • or Perl commands



            perl -i -pe 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file



          • If you have a large number of patterns, it is easier to save your patterns and their replacements in a sed script file:



            #! /usr/bin/sed -f
            s/foo/bar/g
            s/baz/zab/g



          • Or, if you have too many pattern pairs for the above to be feasible, you can read pattern pairs from a file (two space separated patterns, $pattern and $replacement, per line):



            while read -r pattern replacement; do   
            sed -i "s/$pattern/$replacement/" file
            done < patterns.txt



          • That will be quite slow for long lists of patterns and large data files so you might want to read the patterns and create a sed script from them instead. The following assumes a <space> delimiter separates a list of MATCH<space>REPLACE pairs occurring one-per-line in the file patterns.txt :



            sed 's| *([^ ]*) *([^ ]*).*|s/1/2/g|' <patterns.txt |
            sed -f- ./editfile >outfile


            The above format is largely arbitrary and, for example, doesn't allow for a <space> in either of MATCH or REPLACE. The method is very general though: basically, if you can create an output stream which looks like a sed script, then you can source that stream as a sed script by specifying sed's script file as -stdin.




          • You can combine and concatenate multiple scripts in similar fashion:



            SOME_PIPELINE |
            sed -e'#some expression script'
            -f./script_file -f-
            -e'#more inline expressions'
            ./actual_edit_file >./outfile


            A POSIX sed will concatenate all scripts into one in the order they appear on the command-line. None of these need end in a newline.




          • grep can work the same way:



            sed -e'#generate a pattern list' <in |
            grep -f- ./grepped_file



          • When working with fixed-strings as patterns, it is good practice to escape regular expression metacharacters. You can do this rather easily:



            sed 's/[]$&^*./[]/\&/g
            s| *([^ ]*) *([^ ]*).*|s/1/2/g|
            ' <patterns.txt |
            sed -f- ./editfile >outfile



          5. Multiple replace operations: replace multiple patterns with the same string





          • Replace any of foo, bar or baz with foobar



            sed -Ei 's/foo|bar|baz/foobar/g' file



          • or



            perl -i -pe 's/foo|bar|baz/foobar/g' file







          share|improve this answer
















          1. Replacing all occurrences of one string with another in all files in the current directory:



          These are for cases where you know that the directory contains only regular files and that you want to process all non-hidden files. If that is not the case, use the approaches in 2.



          All sed solutions in this answer assume GNU sed. If using FreeBSD or OS/X, replace -i with -i ''. Also note that the use of the -i switch with any version of sed has certain filesystem security implications and is inadvisable in any script which you plan to distribute in any way.





          • Non recursive, files in this directory only:



            sed -i -- 's/foo/bar/g' *
            perl -i -pe 's/foo/bar/g' ./*


            (the perl one will fail for file names ending in | or space)).




          • Recursive, regular files (including hidden ones) in this and all subdirectories



            find . -type f -exec sed -i 's/foo/bar/g' {} +


            If you are using zsh:



            sed -i -- 's/foo/bar/g' **/*(D.)


            (may fail if the list is too big, see zargs to work around).



            Bash can't check directly for regular files, a loop is needed (braces avoid setting the options globally):



            ( shopt -s globstar dotglob;
            for file in **; do
            if [[ -f $file ]] && [[ -w $file ]]; then
            sed -i -- 's/foo/bar/g' "$file"
            fi
            done
            )


            The files are selected when they are actual files (-f) and they are writable (-w).




          2. Replace only if the file name matches another string / has a specific extension / is of a certain type etc:





          • Non-recursive, files in this directory only:



            sed -i -- 's/foo/bar/g' *baz*    ## all files whose name contains baz
            sed -i -- 's/foo/bar/g' *.baz ## files ending in .baz



          • Recursive, regular files in this and all subdirectories



            find . -type f -name "*baz*" -exec sed -i 's/foo/bar/g' {} +


            If you are using bash (braces avoid setting the options globally):



            ( shopt -s globstar dotglob
            sed -i -- 's/foo/bar/g' **baz*
            sed -i -- 's/foo/bar/g' **.baz
            )


            If you are using zsh:



            sed -i -- 's/foo/bar/g' **/*baz*(D.)
            sed -i -- 's/foo/bar/g' **/*.baz(D.)


            The -- serves to tell sed that no more flags will be given in the command line. This is useful to protect against file names starting with -.




          • If a file is of a certain type, for example, executable (see man find for more options):



            find . -type f -executable -exec sed -i 's/foo/bar/g' {} +


            zsh:



            sed -i -- 's/foo/bar/g' **/*(D*)



          3. Replace only if the string is found in a certain context





          • Replace foo with bar only if there is a baz later on the same line:



            sed -i 's/foo(.*baz)/bar1/' file


            In sed, using ( ) saves whatever is in the parentheses and you can then access it with 1. There are many variations of this theme, to learn more about such regular expressions, see here.




          • Replace foo with bar only if foo is found on the 3d column (field) of the input file (assuming whitespace-separated fields):



            gawk -i inplace '{gsub(/foo/,"baz",$3); print}' file


            (needs gawk 4.1.0 or newer).




          • For a different field just use $N where N is the number of the field of interest. For a different field separator (: in this example) use:



            gawk -i inplace -F':' '{gsub(/foo/,"baz",$3);print}' file


            Another solution using perl:



            perl -i -ane '$F[2]=~s/foo/baz/g; $" = " "; print "@Fn"' foo 


            NOTE: both the awk and perl solutions will affect spacing in the file (remove the leading and trailing blanks, and convert sequences of blanks to one space character in those lines that match). For a different field, use $F[N-1] where N is the field number you want and for a different field separator use (the $"=":" sets the output field separator to :):



            perl -i -F':' -ane '$F[2]=~s/foo/baz/g; $"=":";print "@F"' foo 



          • Replace foo with bar only on the 4th line:



            sed -i '4s/foo/bar/g' file
            gawk -i inplace 'NR==4{gsub(/foo/,"baz")};1' file
            perl -i -pe 's/foo/bar/g if $.==4' file



          4. Multiple replace operations: replace with different strings





          • You can combine sed commands:



            sed -i 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file


            Be aware that order matters (sed 's/foo/bar/g; s/bar/baz/g' will substitute foo with baz).




          • or Perl commands



            perl -i -pe 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file



          • If you have a large number of patterns, it is easier to save your patterns and their replacements in a sed script file:



            #! /usr/bin/sed -f
            s/foo/bar/g
            s/baz/zab/g



          • Or, if you have too many pattern pairs for the above to be feasible, you can read pattern pairs from a file (two space separated patterns, $pattern and $replacement, per line):



            while read -r pattern replacement; do   
            sed -i "s/$pattern/$replacement/" file
            done < patterns.txt



          • That will be quite slow for long lists of patterns and large data files so you might want to read the patterns and create a sed script from them instead. The following assumes a <space> delimiter separates a list of MATCH<space>REPLACE pairs occurring one-per-line in the file patterns.txt :



            sed 's| *([^ ]*) *([^ ]*).*|s/1/2/g|' <patterns.txt |
            sed -f- ./editfile >outfile


            The above format is largely arbitrary and, for example, doesn't allow for a <space> in either of MATCH or REPLACE. The method is very general though: basically, if you can create an output stream which looks like a sed script, then you can source that stream as a sed script by specifying sed's script file as -stdin.




          • You can combine and concatenate multiple scripts in similar fashion:



            SOME_PIPELINE |
            sed -e'#some expression script'
            -f./script_file -f-
            -e'#more inline expressions'
            ./actual_edit_file >./outfile


            A POSIX sed will concatenate all scripts into one in the order they appear on the command-line. None of these need end in a newline.




          • grep can work the same way:



            sed -e'#generate a pattern list' <in |
            grep -f- ./grepped_file



          • When working with fixed-strings as patterns, it is good practice to escape regular expression metacharacters. You can do this rather easily:



            sed 's/[]$&^*./[]/\&/g
            s| *([^ ]*) *([^ ]*).*|s/1/2/g|
            ' <patterns.txt |
            sed -f- ./editfile >outfile



          5. Multiple replace operations: replace multiple patterns with the same string





          • Replace any of foo, bar or baz with foobar



            sed -Ei 's/foo|bar|baz/foobar/g' file



          • or



            perl -i -pe 's/foo|bar|baz/foobar/g' file








          share|improve this answer















          share|improve this answer




          share|improve this answer



          share|improve this answer








          edited Apr 13 '17 at 12:37


























          community wiki





          31 revs, 9 users 58%
          terdon












          • 2





            @StéphaneChazelas thanks for the edit, it did indeed fix several things. However, please don't remove information that is relevant to bash. Not everyone uses zsh. By all means add zsh info but there is no reason to remove the bash stuff. Also, I know that using the shell for text processing is not ideal but there are cases where it is needed. I edited in a better version of my original script that will create a sed script instead of actually using the shell loop to parse. This can be useful if you have several hundred pairs of patterns for example.

            – terdon
            Jan 16 '15 at 15:10






          • 2





            @terdon, your bash one is incorrect. bash before 4.3 will follow symlinks when descending. Also bash has no equivalent for the (.) globbing qualifier so can't be used here. (you're missing some -- as well). The for loop is incorrect (missing -r) and means making several passes in the files and adds no benefit over a sed script.

            – Stéphane Chazelas
            Jan 16 '15 at 15:16






          • 7





            @terdon What does -- after sed -i and before the substitute command indicate?

            – Geek
            Sep 28 '15 at 11:29








          • 5





            @Geek that's a POSIX thing. It signifies the end of options and lets you pass arguments starting with -. Using it ensures that the commands will work on files with names like -foo. Without it, the -f would be parsed as an option.

            – terdon
            Sep 28 '15 at 11:42








          • 1





            Be very careful executing some of the recursive commands in git repositories. For example, the solutions provided in section 1 of this answer will actually modify internal git files in a .git directory, and actually mess up your checkout. Better to operate within/on specific directories by name.

            – Pistos
            Apr 19 '16 at 14:44














          • 2





            @StéphaneChazelas thanks for the edit, it did indeed fix several things. However, please don't remove information that is relevant to bash. Not everyone uses zsh. By all means add zsh info but there is no reason to remove the bash stuff. Also, I know that using the shell for text processing is not ideal but there are cases where it is needed. I edited in a better version of my original script that will create a sed script instead of actually using the shell loop to parse. This can be useful if you have several hundred pairs of patterns for example.

            – terdon
            Jan 16 '15 at 15:10






          • 2





            @terdon, your bash one is incorrect. bash before 4.3 will follow symlinks when descending. Also bash has no equivalent for the (.) globbing qualifier so can't be used here. (you're missing some -- as well). The for loop is incorrect (missing -r) and means making several passes in the files and adds no benefit over a sed script.

            – Stéphane Chazelas
            Jan 16 '15 at 15:16






          • 7





            @terdon What does -- after sed -i and before the substitute command indicate?

            – Geek
            Sep 28 '15 at 11:29








          • 5





            @Geek that's a POSIX thing. It signifies the end of options and lets you pass arguments starting with -. Using it ensures that the commands will work on files with names like -foo. Without it, the -f would be parsed as an option.

            – terdon
            Sep 28 '15 at 11:42








          • 1





            Be very careful executing some of the recursive commands in git repositories. For example, the solutions provided in section 1 of this answer will actually modify internal git files in a .git directory, and actually mess up your checkout. Better to operate within/on specific directories by name.

            – Pistos
            Apr 19 '16 at 14:44








          2




          2





          @StéphaneChazelas thanks for the edit, it did indeed fix several things. However, please don't remove information that is relevant to bash. Not everyone uses zsh. By all means add zsh info but there is no reason to remove the bash stuff. Also, I know that using the shell for text processing is not ideal but there are cases where it is needed. I edited in a better version of my original script that will create a sed script instead of actually using the shell loop to parse. This can be useful if you have several hundred pairs of patterns for example.

          – terdon
          Jan 16 '15 at 15:10





          @StéphaneChazelas thanks for the edit, it did indeed fix several things. However, please don't remove information that is relevant to bash. Not everyone uses zsh. By all means add zsh info but there is no reason to remove the bash stuff. Also, I know that using the shell for text processing is not ideal but there are cases where it is needed. I edited in a better version of my original script that will create a sed script instead of actually using the shell loop to parse. This can be useful if you have several hundred pairs of patterns for example.

          – terdon
          Jan 16 '15 at 15:10




          2




          2





          @terdon, your bash one is incorrect. bash before 4.3 will follow symlinks when descending. Also bash has no equivalent for the (.) globbing qualifier so can't be used here. (you're missing some -- as well). The for loop is incorrect (missing -r) and means making several passes in the files and adds no benefit over a sed script.

          – Stéphane Chazelas
          Jan 16 '15 at 15:16





          @terdon, your bash one is incorrect. bash before 4.3 will follow symlinks when descending. Also bash has no equivalent for the (.) globbing qualifier so can't be used here. (you're missing some -- as well). The for loop is incorrect (missing -r) and means making several passes in the files and adds no benefit over a sed script.

          – Stéphane Chazelas
          Jan 16 '15 at 15:16




          7




          7





          @terdon What does -- after sed -i and before the substitute command indicate?

          – Geek
          Sep 28 '15 at 11:29







          @terdon What does -- after sed -i and before the substitute command indicate?

          – Geek
          Sep 28 '15 at 11:29






          5




          5





          @Geek that's a POSIX thing. It signifies the end of options and lets you pass arguments starting with -. Using it ensures that the commands will work on files with names like -foo. Without it, the -f would be parsed as an option.

          – terdon
          Sep 28 '15 at 11:42







          @Geek that's a POSIX thing. It signifies the end of options and lets you pass arguments starting with -. Using it ensures that the commands will work on files with names like -foo. Without it, the -f would be parsed as an option.

          – terdon
          Sep 28 '15 at 11:42






          1




          1





          Be very careful executing some of the recursive commands in git repositories. For example, the solutions provided in section 1 of this answer will actually modify internal git files in a .git directory, and actually mess up your checkout. Better to operate within/on specific directories by name.

          – Pistos
          Apr 19 '16 at 14:44





          Be very careful executing some of the recursive commands in git repositories. For example, the solutions provided in section 1 of this answer will actually modify internal git files in a .git directory, and actually mess up your checkout. Better to operate within/on specific directories by name.

          – Pistos
          Apr 19 '16 at 14:44













          75


















          A good replacement Linux tool is rpl, that was originally written for the Debian project, so it is available with apt-get install rpl in any Debian derived distro, and may be for others, but otherwise you can download the tar.gz file in SourgeForge.



          Simplest example of use:



           $ rpl old_string new_string test.txt


          Note that if the string contain spaces it should be enclosed in quotation marks. By default rpl take care of capital letters but not of complete words, but you can change these defaults with options -i (ignore case) and -w (whole words). You can also specify multiple files:



           $ rpl -i -w "old string" "new string" test.txt test2.txt


          Or even specify the extensions (-x) to search or even search recursively (-R) in the directory:



           $ rpl -x .html -x .txt -R old_string new_string test*


          You can also search/replace in interactive mode with -p (prompt) option:



          The output show the numbers of files/string replaced and the type of search (case in/sensitive, whole/partial words), but it can be silent with the -q (quiet mode) option, or even more verbose, listing line numbers that contain matches of each file and directory with -v (verbose mode) option.



          Other options that are worth remembering are -e (honor escapes) that allow regular expressions, so you can search also tabs (t), new lines (n),etc. Even you can use -f to force permissions (of course, only when the user have write permissions) and -d to preserve the modification times`).



          Finally, if you are unsure of which will make exactly, use the -s (simulate mode).






          share|improve this answer
























          • 2





            So much better at the feedback and simplicity than sed. I just wish it allowed acting on file names, and then it'd be perfect as-is.

            – Kzqai
            Dec 23 '16 at 17:12






          • 1





            i like the -s (simulate mode) :-)

            – erm3nda
            Jun 10 '18 at 11:08
















          75


















          A good replacement Linux tool is rpl, that was originally written for the Debian project, so it is available with apt-get install rpl in any Debian derived distro, and may be for others, but otherwise you can download the tar.gz file in SourgeForge.



          Simplest example of use:



           $ rpl old_string new_string test.txt


          Note that if the string contain spaces it should be enclosed in quotation marks. By default rpl take care of capital letters but not of complete words, but you can change these defaults with options -i (ignore case) and -w (whole words). You can also specify multiple files:



           $ rpl -i -w "old string" "new string" test.txt test2.txt


          Or even specify the extensions (-x) to search or even search recursively (-R) in the directory:



           $ rpl -x .html -x .txt -R old_string new_string test*


          You can also search/replace in interactive mode with -p (prompt) option:



          The output show the numbers of files/string replaced and the type of search (case in/sensitive, whole/partial words), but it can be silent with the -q (quiet mode) option, or even more verbose, listing line numbers that contain matches of each file and directory with -v (verbose mode) option.



          Other options that are worth remembering are -e (honor escapes) that allow regular expressions, so you can search also tabs (t), new lines (n),etc. Even you can use -f to force permissions (of course, only when the user have write permissions) and -d to preserve the modification times`).



          Finally, if you are unsure of which will make exactly, use the -s (simulate mode).






          share|improve this answer
























          • 2





            So much better at the feedback and simplicity than sed. I just wish it allowed acting on file names, and then it'd be perfect as-is.

            – Kzqai
            Dec 23 '16 at 17:12






          • 1





            i like the -s (simulate mode) :-)

            – erm3nda
            Jun 10 '18 at 11:08














          75














          75










          75









          A good replacement Linux tool is rpl, that was originally written for the Debian project, so it is available with apt-get install rpl in any Debian derived distro, and may be for others, but otherwise you can download the tar.gz file in SourgeForge.



          Simplest example of use:



           $ rpl old_string new_string test.txt


          Note that if the string contain spaces it should be enclosed in quotation marks. By default rpl take care of capital letters but not of complete words, but you can change these defaults with options -i (ignore case) and -w (whole words). You can also specify multiple files:



           $ rpl -i -w "old string" "new string" test.txt test2.txt


          Or even specify the extensions (-x) to search or even search recursively (-R) in the directory:



           $ rpl -x .html -x .txt -R old_string new_string test*


          You can also search/replace in interactive mode with -p (prompt) option:



          The output show the numbers of files/string replaced and the type of search (case in/sensitive, whole/partial words), but it can be silent with the -q (quiet mode) option, or even more verbose, listing line numbers that contain matches of each file and directory with -v (verbose mode) option.



          Other options that are worth remembering are -e (honor escapes) that allow regular expressions, so you can search also tabs (t), new lines (n),etc. Even you can use -f to force permissions (of course, only when the user have write permissions) and -d to preserve the modification times`).



          Finally, if you are unsure of which will make exactly, use the -s (simulate mode).






          share|improve this answer
















          A good replacement Linux tool is rpl, that was originally written for the Debian project, so it is available with apt-get install rpl in any Debian derived distro, and may be for others, but otherwise you can download the tar.gz file in SourgeForge.



          Simplest example of use:



           $ rpl old_string new_string test.txt


          Note that if the string contain spaces it should be enclosed in quotation marks. By default rpl take care of capital letters but not of complete words, but you can change these defaults with options -i (ignore case) and -w (whole words). You can also specify multiple files:



           $ rpl -i -w "old string" "new string" test.txt test2.txt


          Or even specify the extensions (-x) to search or even search recursively (-R) in the directory:



           $ rpl -x .html -x .txt -R old_string new_string test*


          You can also search/replace in interactive mode with -p (prompt) option:



          The output show the numbers of files/string replaced and the type of search (case in/sensitive, whole/partial words), but it can be silent with the -q (quiet mode) option, or even more verbose, listing line numbers that contain matches of each file and directory with -v (verbose mode) option.



          Other options that are worth remembering are -e (honor escapes) that allow regular expressions, so you can search also tabs (t), new lines (n),etc. Even you can use -f to force permissions (of course, only when the user have write permissions) and -d to preserve the modification times`).



          Finally, if you are unsure of which will make exactly, use the -s (simulate mode).







          share|improve this answer















          share|improve this answer




          share|improve this answer



          share|improve this answer








          edited Jan 5 '16 at 20:01

























          answered Dec 27 '15 at 8:06









          FranFran

          1,21111 silver badges8 bronze badges




          1,21111 silver badges8 bronze badges











          • 2





            So much better at the feedback and simplicity than sed. I just wish it allowed acting on file names, and then it'd be perfect as-is.

            – Kzqai
            Dec 23 '16 at 17:12






          • 1





            i like the -s (simulate mode) :-)

            – erm3nda
            Jun 10 '18 at 11:08














          • 2





            So much better at the feedback and simplicity than sed. I just wish it allowed acting on file names, and then it'd be perfect as-is.

            – Kzqai
            Dec 23 '16 at 17:12






          • 1





            i like the -s (simulate mode) :-)

            – erm3nda
            Jun 10 '18 at 11:08








          2




          2





          So much better at the feedback and simplicity than sed. I just wish it allowed acting on file names, and then it'd be perfect as-is.

          – Kzqai
          Dec 23 '16 at 17:12





          So much better at the feedback and simplicity than sed. I just wish it allowed acting on file names, and then it'd be perfect as-is.

          – Kzqai
          Dec 23 '16 at 17:12




          1




          1





          i like the -s (simulate mode) :-)

          – erm3nda
          Jun 10 '18 at 11:08





          i like the -s (simulate mode) :-)

          – erm3nda
          Jun 10 '18 at 11:08











          25


















          How to do a search and replace over multiple files suggests:




          You could also use find and sed, but I find that this little line of
          perl works nicely.



          perl -pi -w -e 's/search/replace/g;' *.php



          • -e means execute the following line of code.

          • -i means edit in-place

          • -w write warnings

          • -p loop over the input file, printing each line after the script is applied to it.




          My best results come from using perl and grep (to ensure that file have the search expression )



          perl -pi -w -e 's/search/replace/g;' $( grep -rl 'search' )





          share|improve this answer

































            25


















            How to do a search and replace over multiple files suggests:




            You could also use find and sed, but I find that this little line of
            perl works nicely.



            perl -pi -w -e 's/search/replace/g;' *.php



            • -e means execute the following line of code.

            • -i means edit in-place

            • -w write warnings

            • -p loop over the input file, printing each line after the script is applied to it.




            My best results come from using perl and grep (to ensure that file have the search expression )



            perl -pi -w -e 's/search/replace/g;' $( grep -rl 'search' )





            share|improve this answer































              25














              25










              25









              How to do a search and replace over multiple files suggests:




              You could also use find and sed, but I find that this little line of
              perl works nicely.



              perl -pi -w -e 's/search/replace/g;' *.php



              • -e means execute the following line of code.

              • -i means edit in-place

              • -w write warnings

              • -p loop over the input file, printing each line after the script is applied to it.




              My best results come from using perl and grep (to ensure that file have the search expression )



              perl -pi -w -e 's/search/replace/g;' $( grep -rl 'search' )





              share|improve this answer
















              How to do a search and replace over multiple files suggests:




              You could also use find and sed, but I find that this little line of
              perl works nicely.



              perl -pi -w -e 's/search/replace/g;' *.php



              • -e means execute the following line of code.

              • -i means edit in-place

              • -w write warnings

              • -p loop over the input file, printing each line after the script is applied to it.




              My best results come from using perl and grep (to ensure that file have the search expression )



              perl -pi -w -e 's/search/replace/g;' $( grep -rl 'search' )






              share|improve this answer















              share|improve this answer




              share|improve this answer



              share|improve this answer








              edited Oct 19 '15 at 21:38









              Community

              1




              1










              answered Jan 16 '15 at 14:02









              Alejandro Salamanca MazueloAlejandro Salamanca Mazuelo

              3784 silver badges8 bronze badges




              3784 silver badges8 bronze badges


























                  13


















                  You can use Vim in Ex mode:




                  replace string ALF with BRA in all files in the current directory?




                  for CHA in *
                  do
                  ex -sc '%s/ALF/BRA/g' -cx "$CHA"
                  done





                  do the same recursively for sub directories?




                  find -type f -exec ex -sc '%s/ALF/BRA/g' -cx {} ';'





                  replace only if the file name matches another string?




                  for CHA in *.txt
                  do
                  ex -sc '%s/ALF/BRA/g' -cx "$CHA"
                  done





                  replace only if the string is found in a certain context?




                  ex -sc 'g/DEL/s/ALF/BRA/g' -cx file





                  replace if the string is on a certain line number?




                  ex -sc '2s/ALF/BRA/g' -cx file





                  replace multiple strings with the same replacement




                  ex -sc '%s/vALF|ECH/BRA/g' -cx file





                  replace multiple strings with different replacements




                  ex -sc '%s/ALF/BRA/g|%s/FOX/GOL/g' -cx file





                  share|improve this answer

































                    13


















                    You can use Vim in Ex mode:




                    replace string ALF with BRA in all files in the current directory?




                    for CHA in *
                    do
                    ex -sc '%s/ALF/BRA/g' -cx "$CHA"
                    done





                    do the same recursively for sub directories?




                    find -type f -exec ex -sc '%s/ALF/BRA/g' -cx {} ';'





                    replace only if the file name matches another string?




                    for CHA in *.txt
                    do
                    ex -sc '%s/ALF/BRA/g' -cx "$CHA"
                    done





                    replace only if the string is found in a certain context?




                    ex -sc 'g/DEL/s/ALF/BRA/g' -cx file





                    replace if the string is on a certain line number?




                    ex -sc '2s/ALF/BRA/g' -cx file





                    replace multiple strings with the same replacement




                    ex -sc '%s/vALF|ECH/BRA/g' -cx file





                    replace multiple strings with different replacements




                    ex -sc '%s/ALF/BRA/g|%s/FOX/GOL/g' -cx file





                    share|improve this answer































                      13














                      13










                      13









                      You can use Vim in Ex mode:




                      replace string ALF with BRA in all files in the current directory?




                      for CHA in *
                      do
                      ex -sc '%s/ALF/BRA/g' -cx "$CHA"
                      done





                      do the same recursively for sub directories?




                      find -type f -exec ex -sc '%s/ALF/BRA/g' -cx {} ';'





                      replace only if the file name matches another string?




                      for CHA in *.txt
                      do
                      ex -sc '%s/ALF/BRA/g' -cx "$CHA"
                      done





                      replace only if the string is found in a certain context?




                      ex -sc 'g/DEL/s/ALF/BRA/g' -cx file





                      replace if the string is on a certain line number?




                      ex -sc '2s/ALF/BRA/g' -cx file





                      replace multiple strings with the same replacement




                      ex -sc '%s/vALF|ECH/BRA/g' -cx file





                      replace multiple strings with different replacements




                      ex -sc '%s/ALF/BRA/g|%s/FOX/GOL/g' -cx file





                      share|improve this answer
















                      You can use Vim in Ex mode:




                      replace string ALF with BRA in all files in the current directory?




                      for CHA in *
                      do
                      ex -sc '%s/ALF/BRA/g' -cx "$CHA"
                      done





                      do the same recursively for sub directories?




                      find -type f -exec ex -sc '%s/ALF/BRA/g' -cx {} ';'





                      replace only if the file name matches another string?




                      for CHA in *.txt
                      do
                      ex -sc '%s/ALF/BRA/g' -cx "$CHA"
                      done





                      replace only if the string is found in a certain context?




                      ex -sc 'g/DEL/s/ALF/BRA/g' -cx file





                      replace if the string is on a certain line number?




                      ex -sc '2s/ALF/BRA/g' -cx file





                      replace multiple strings with the same replacement




                      ex -sc '%s/vALF|ECH/BRA/g' -cx file





                      replace multiple strings with different replacements




                      ex -sc '%s/ALF/BRA/g|%s/FOX/GOL/g' -cx file






                      share|improve this answer















                      share|improve this answer




                      share|improve this answer



                      share|improve this answer








                      edited Apr 17 '16 at 5:31

























                      answered Apr 17 '16 at 1:47









                      Steven PennySteven Penny

                      1




                      1


























                          13


















                          I used this:



                          grep -r "old_string" -l | tr 'n' ' ' | xargs sed -i 's/old_string/new_string/g'



                          1. List all files that contain old_string.


                          2. Replace newline in result with spaces (so that the list of files can be fed to sed.


                          3. Run sed on those files to replace old string with new.



                          Update: The above result will fail on filenames that contain whitespaces. Instead, use:



                          grep --null -lr "old_string" | xargs --null sed -i 's/old_string/new_string/g'






                          share|improve this answer





























                          • Note that this will fail if any of your file names contain spaces, tabs or newlines. Use grep --null -lr "old_string" | xargs --null sed -i 's/old_string/new_string/g' will make it deal with arbitrary file names.

                            – terdon
                            Oct 26 '15 at 17:07













                          • thanks guys. added update and left the old code cause it's an interesting caveat that could be useful to someone unaware of this behavior.

                            – o_o_o--
                            Oct 26 '15 at 20:59


















                          13


















                          I used this:



                          grep -r "old_string" -l | tr 'n' ' ' | xargs sed -i 's/old_string/new_string/g'



                          1. List all files that contain old_string.


                          2. Replace newline in result with spaces (so that the list of files can be fed to sed.


                          3. Run sed on those files to replace old string with new.



                          Update: The above result will fail on filenames that contain whitespaces. Instead, use:



                          grep --null -lr "old_string" | xargs --null sed -i 's/old_string/new_string/g'






                          share|improve this answer





























                          • Note that this will fail if any of your file names contain spaces, tabs or newlines. Use grep --null -lr "old_string" | xargs --null sed -i 's/old_string/new_string/g' will make it deal with arbitrary file names.

                            – terdon
                            Oct 26 '15 at 17:07













                          • thanks guys. added update and left the old code cause it's an interesting caveat that could be useful to someone unaware of this behavior.

                            – o_o_o--
                            Oct 26 '15 at 20:59
















                          13














                          13










                          13









                          I used this:



                          grep -r "old_string" -l | tr 'n' ' ' | xargs sed -i 's/old_string/new_string/g'



                          1. List all files that contain old_string.


                          2. Replace newline in result with spaces (so that the list of files can be fed to sed.


                          3. Run sed on those files to replace old string with new.



                          Update: The above result will fail on filenames that contain whitespaces. Instead, use:



                          grep --null -lr "old_string" | xargs --null sed -i 's/old_string/new_string/g'






                          share|improve this answer
















                          I used this:



                          grep -r "old_string" -l | tr 'n' ' ' | xargs sed -i 's/old_string/new_string/g'



                          1. List all files that contain old_string.


                          2. Replace newline in result with spaces (so that the list of files can be fed to sed.


                          3. Run sed on those files to replace old string with new.



                          Update: The above result will fail on filenames that contain whitespaces. Instead, use:



                          grep --null -lr "old_string" | xargs --null sed -i 's/old_string/new_string/g'







                          share|improve this answer















                          share|improve this answer




                          share|improve this answer



                          share|improve this answer








                          edited May 9 '16 at 7:34









                          shas

                          7383 gold badges10 silver badges27 bronze badges




                          7383 gold badges10 silver badges27 bronze badges










                          answered Oct 26 '15 at 16:58









                          o_o_o--o_o_o--

                          4201 gold badge4 silver badges9 bronze badges




                          4201 gold badge4 silver badges9 bronze badges
















                          • Note that this will fail if any of your file names contain spaces, tabs or newlines. Use grep --null -lr "old_string" | xargs --null sed -i 's/old_string/new_string/g' will make it deal with arbitrary file names.

                            – terdon
                            Oct 26 '15 at 17:07













                          • thanks guys. added update and left the old code cause it's an interesting caveat that could be useful to someone unaware of this behavior.

                            – o_o_o--
                            Oct 26 '15 at 20:59





















                          • Note that this will fail if any of your file names contain spaces, tabs or newlines. Use grep --null -lr "old_string" | xargs --null sed -i 's/old_string/new_string/g' will make it deal with arbitrary file names.

                            – terdon
                            Oct 26 '15 at 17:07













                          • thanks guys. added update and left the old code cause it's an interesting caveat that could be useful to someone unaware of this behavior.

                            – o_o_o--
                            Oct 26 '15 at 20:59



















                          Note that this will fail if any of your file names contain spaces, tabs or newlines. Use grep --null -lr "old_string" | xargs --null sed -i 's/old_string/new_string/g' will make it deal with arbitrary file names.

                          – terdon
                          Oct 26 '15 at 17:07







                          Note that this will fail if any of your file names contain spaces, tabs or newlines. Use grep --null -lr "old_string" | xargs --null sed -i 's/old_string/new_string/g' will make it deal with arbitrary file names.

                          – terdon
                          Oct 26 '15 at 17:07















                          thanks guys. added update and left the old code cause it's an interesting caveat that could be useful to someone unaware of this behavior.

                          – o_o_o--
                          Oct 26 '15 at 20:59







                          thanks guys. added update and left the old code cause it's an interesting caveat that could be useful to someone unaware of this behavior.

                          – o_o_o--
                          Oct 26 '15 at 20:59













                          6


















                          From a user's perspective, a nice & simple Unix tool that does the job perfectly is qsubst. For example,



                          % qsubst foo bar *.c *.h


                          will replace foo with bar in all my C files. A nice feature is that qsubst will do a query-replace, i.e., it will show me each occurrence of foo and ask whether I want to replace it or not. [You can replace unconditionally (no asking) with -go option, and there are other options, e.g., -w if you only want to replace foo when it is a whole word.]



                          How to get it: qsubst was invented by der Mouse (from McGill) and posted to comp.unix.sources 11(7) in Aug. 1987. Updated versions exist. For example, the NetBSD version qsubst.c,v 1.8 2004/11/01 compiles and runs perfectly on my mac.






                          share|improve this answer

































                            6


















                            From a user's perspective, a nice & simple Unix tool that does the job perfectly is qsubst. For example,



                            % qsubst foo bar *.c *.h


                            will replace foo with bar in all my C files. A nice feature is that qsubst will do a query-replace, i.e., it will show me each occurrence of foo and ask whether I want to replace it or not. [You can replace unconditionally (no asking) with -go option, and there are other options, e.g., -w if you only want to replace foo when it is a whole word.]



                            How to get it: qsubst was invented by der Mouse (from McGill) and posted to comp.unix.sources 11(7) in Aug. 1987. Updated versions exist. For example, the NetBSD version qsubst.c,v 1.8 2004/11/01 compiles and runs perfectly on my mac.






                            share|improve this answer































                              6














                              6










                              6









                              From a user's perspective, a nice & simple Unix tool that does the job perfectly is qsubst. For example,



                              % qsubst foo bar *.c *.h


                              will replace foo with bar in all my C files. A nice feature is that qsubst will do a query-replace, i.e., it will show me each occurrence of foo and ask whether I want to replace it or not. [You can replace unconditionally (no asking) with -go option, and there are other options, e.g., -w if you only want to replace foo when it is a whole word.]



                              How to get it: qsubst was invented by der Mouse (from McGill) and posted to comp.unix.sources 11(7) in Aug. 1987. Updated versions exist. For example, the NetBSD version qsubst.c,v 1.8 2004/11/01 compiles and runs perfectly on my mac.






                              share|improve this answer
















                              From a user's perspective, a nice & simple Unix tool that does the job perfectly is qsubst. For example,



                              % qsubst foo bar *.c *.h


                              will replace foo with bar in all my C files. A nice feature is that qsubst will do a query-replace, i.e., it will show me each occurrence of foo and ask whether I want to replace it or not. [You can replace unconditionally (no asking) with -go option, and there are other options, e.g., -w if you only want to replace foo when it is a whole word.]



                              How to get it: qsubst was invented by der Mouse (from McGill) and posted to comp.unix.sources 11(7) in Aug. 1987. Updated versions exist. For example, the NetBSD version qsubst.c,v 1.8 2004/11/01 compiles and runs perfectly on my mac.







                              share|improve this answer















                              share|improve this answer




                              share|improve this answer



                              share|improve this answer








                              edited Jul 30 '15 at 11:27









                              terdon

                              143k35 gold badges296 silver badges473 bronze badges




                              143k35 gold badges296 silver badges473 bronze badges










                              answered Jul 30 '15 at 11:25









                              phsphs

                              2832 silver badges9 bronze badges




                              2832 silver badges9 bronze badges


























                                  2


















                                  I needed something that would provide a dry-run option and would work recursively with a glob, and after trying to do it with awk and sed I gave up and instead did it in python.



                                  The script searches recursively all files matching a glob pattern (e.g. --glob="*.html") for a regex and replaces with the replacement regex:



                                  find_replace.py [--dir=my_folder] 
                                  --search-regex=<search_regex>
                                  --replace-regex=<replace_regex>
                                  --glob=[glob_pattern]
                                  --dry-run


                                  Every long option such as --search-regex has a corresponding short option, i.e. -s. Run with -h to see all options.



                                  For example, this will flip all dates from 2017-12-31 to 31-12-2017:



                                  python replace.py --glob=myfile.txt 
                                  --search-regex="(d{4})-(d{2})-(d{2})"
                                  --replace-regex="3-2-1"
                                  --dry-run --verbose


                                  import os
                                  import fnmatch
                                  import sys
                                  import shutil
                                  import re

                                  import argparse

                                  def find_replace(cfg):
                                  search_pattern = re.compile(cfg.search_regex)

                                  if cfg.dry_run:
                                  print('THIS IS A DRY RUN -- NO FILES WILL BE CHANGED!')

                                  for path, dirs, files in os.walk(os.path.abspath(cfg.dir)):
                                  for filename in fnmatch.filter(files, cfg.glob):

                                  if cfg.print_parent_folder:
                                  pardir = os.path.normpath(os.path.join(path, '..'))
                                  pardir = os.path.split(pardir)[-1]
                                  print('[%s]' % pardir)
                                  filepath = os.path.join(path, filename)

                                  # backup original file
                                  if cfg.create_backup:
                                  backup_path = filepath + '.bak'

                                  while os.path.exists(backup_path):
                                  backup_path += '.bak'
                                  print('DBG: creating backup', backup_path)
                                  shutil.copyfile(filepath, backup_path)

                                  with open(filepath) as f:
                                  old_text = f.read()

                                  all_matches = search_pattern.findall(old_text)

                                  if all_matches:

                                  print('Found {} matches in file {}'.format(len(all_matches), filename))

                                  new_text = search_pattern.sub(cfg.replace_regex, old_text)

                                  if not cfg.dry_run:
                                  with open(filepath, "w") as f:
                                  print('DBG: replacing in file', filepath)
                                  f.write(new_text)
                                  else:
                                  for idx, matches in enumerate(all_matches):
                                  print("Match #{}: {}".format(idx, matches))

                                  print("NEW TEXT:n{}".format(new_text))

                                  elif cfg.verbose:
                                  print('File {} does not contain search regex "{}"'.format(filename, cfg.search_regex))


                                  if __name__ == '__main__':

                                  parser = argparse.ArgumentParser(description='''DESCRIPTION:
                                  Find and replace recursively from the given folder using regular expressions''',
                                  formatter_class=argparse.RawDescriptionHelpFormatter,
                                  epilog='''USAGE:
                                  {0} -d [my_folder] -s <search_regex> -r <replace_regex> -g [glob_pattern]

                                  '''.format(os.path.basename(sys.argv[0])))

                                  parser.add_argument('--dir', '-d',
                                  help='folder to search in; by default current folder',
                                  default='.')

                                  parser.add_argument('--search-regex', '-s',
                                  help='search regex',
                                  required=True)

                                  parser.add_argument('--replace-regex', '-r',
                                  help='replacement regex',
                                  required=True)

                                  parser.add_argument('--glob', '-g',
                                  help='glob pattern, i.e. *.html',
                                  default="*.*")

                                  parser.add_argument('--dry-run', '-dr',
                                  action='store_true',
                                  help="don't replace anything just show what is going to be done",
                                  default=False)

                                  parser.add_argument('--create-backup', '-b',
                                  action='store_true',
                                  help='Create backup files',
                                  default=False)

                                  parser.add_argument('--verbose', '-v',
                                  action='store_true',
                                  help="Show files which don't match the search regex",
                                  default=False)

                                  parser.add_argument('--print-parent-folder', '-p',
                                  action='store_true',
                                  help="Show the parent info for debug",
                                  default=False)

                                  config = parser.parse_args(sys.argv[1:])

                                  find_replace(config)



                                  Here is an updated version of the script which highlights the search terms and replacements with different colors.







                                  share|improve this answer
























                                  • 1





                                    I don't understand why you would make something this complex. For recursion, use either bash's (or your shell's equivalent) globstar option and ** globs or find. For a dry run, just use sed. Unless you use the -i option, it won't make any changes. For a backup use sed -i.bak (or perl -i .bak); for files that don't match, use grep PATTERN file || echo file. And why in the world would you have python expand the glob instead of letting the shell do it? Why script.py --glob=foo* instead of just script.py foo*?

                                    – terdon
                                    Nov 23 '17 at 9:34








                                  • 1





                                    My why's are very simple: (1) above all, ease of debugging; (2) using only a single well documented tool with a supportive community (3) not knowing sed and awk well and being unwilling to invest extra time on mastering them, (4) readability, (5) this solution will also work on non-posix systems (not that I need that but somebody else might).

                                    – ccpizza
                                    Nov 23 '17 at 12:59


















                                  2


















                                  I needed something that would provide a dry-run option and would work recursively with a glob, and after trying to do it with awk and sed I gave up and instead did it in python.



                                  The script searches recursively all files matching a glob pattern (e.g. --glob="*.html") for a regex and replaces with the replacement regex:



                                  find_replace.py [--dir=my_folder] 
                                  --search-regex=<search_regex>
                                  --replace-regex=<replace_regex>
                                  --glob=[glob_pattern]
                                  --dry-run


                                  Every long option such as --search-regex has a corresponding short option, i.e. -s. Run with -h to see all options.



                                  For example, this will flip all dates from 2017-12-31 to 31-12-2017:



                                  python replace.py --glob=myfile.txt 
                                  --search-regex="(d{4})-(d{2})-(d{2})"
                                  --replace-regex="3-2-1"
                                  --dry-run --verbose


                                  import os
                                  import fnmatch
                                  import sys
                                  import shutil
                                  import re

                                  import argparse

                                  def find_replace(cfg):
                                  search_pattern = re.compile(cfg.search_regex)

                                  if cfg.dry_run:
                                  print('THIS IS A DRY RUN -- NO FILES WILL BE CHANGED!')

                                  for path, dirs, files in os.walk(os.path.abspath(cfg.dir)):
                                  for filename in fnmatch.filter(files, cfg.glob):

                                  if cfg.print_parent_folder:
                                  pardir = os.path.normpath(os.path.join(path, '..'))
                                  pardir = os.path.split(pardir)[-1]
                                  print('[%s]' % pardir)
                                  filepath = os.path.join(path, filename)

                                  # backup original file
                                  if cfg.create_backup:
                                  backup_path = filepath + '.bak'

                                  while os.path.exists(backup_path):
                                  backup_path += '.bak'
                                  print('DBG: creating backup', backup_path)
                                  shutil.copyfile(filepath, backup_path)

                                  with open(filepath) as f:
                                  old_text = f.read()

                                  all_matches = search_pattern.findall(old_text)

                                  if all_matches:

                                  print('Found {} matches in file {}'.format(len(all_matches), filename))

                                  new_text = search_pattern.sub(cfg.replace_regex, old_text)

                                  if not cfg.dry_run:
                                  with open(filepath, "w") as f:
                                  print('DBG: replacing in file', filepath)
                                  f.write(new_text)
                                  else:
                                  for idx, matches in enumerate(all_matches):
                                  print("Match #{}: {}".format(idx, matches))

                                  print("NEW TEXT:n{}".format(new_text))

                                  elif cfg.verbose:
                                  print('File {} does not contain search regex "{}"'.format(filename, cfg.search_regex))


                                  if __name__ == '__main__':

                                  parser = argparse.ArgumentParser(description='''DESCRIPTION:
                                  Find and replace recursively from the given folder using regular expressions''',
                                  formatter_class=argparse.RawDescriptionHelpFormatter,
                                  epilog='''USAGE:
                                  {0} -d [my_folder] -s <search_regex> -r <replace_regex> -g [glob_pattern]

                                  '''.format(os.path.basename(sys.argv[0])))

                                  parser.add_argument('--dir', '-d',
                                  help='folder to search in; by default current folder',
                                  default='.')

                                  parser.add_argument('--search-regex', '-s',
                                  help='search regex',
                                  required=True)

                                  parser.add_argument('--replace-regex', '-r',
                                  help='replacement regex',
                                  required=True)

                                  parser.add_argument('--glob', '-g',
                                  help='glob pattern, i.e. *.html',
                                  default="*.*")

                                  parser.add_argument('--dry-run', '-dr',
                                  action='store_true',
                                  help="don't replace anything just show what is going to be done",
                                  default=False)

                                  parser.add_argument('--create-backup', '-b',
                                  action='store_true',
                                  help='Create backup files',
                                  default=False)

                                  parser.add_argument('--verbose', '-v',
                                  action='store_true',
                                  help="Show files which don't match the search regex",
                                  default=False)

                                  parser.add_argument('--print-parent-folder', '-p',
                                  action='store_true',
                                  help="Show the parent info for debug",
                                  default=False)

                                  config = parser.parse_args(sys.argv[1:])

                                  find_replace(config)



                                  Here is an updated version of the script which highlights the search terms and replacements with different colors.







                                  share|improve this answer
























                                  • 1





                                    I don't understand why you would make something this complex. For recursion, use either bash's (or your shell's equivalent) globstar option and ** globs or find. For a dry run, just use sed. Unless you use the -i option, it won't make any changes. For a backup use sed -i.bak (or perl -i .bak); for files that don't match, use grep PATTERN file || echo file. And why in the world would you have python expand the glob instead of letting the shell do it? Why script.py --glob=foo* instead of just script.py foo*?

                                    – terdon
                                    Nov 23 '17 at 9:34








                                  • 1





                                    My why's are very simple: (1) above all, ease of debugging; (2) using only a single well documented tool with a supportive community (3) not knowing sed and awk well and being unwilling to invest extra time on mastering them, (4) readability, (5) this solution will also work on non-posix systems (not that I need that but somebody else might).

                                    – ccpizza
                                    Nov 23 '17 at 12:59
















                                  2














                                  2










                                  2









                                  I needed something that would provide a dry-run option and would work recursively with a glob, and after trying to do it with awk and sed I gave up and instead did it in python.



                                  The script searches recursively all files matching a glob pattern (e.g. --glob="*.html") for a regex and replaces with the replacement regex:



                                  find_replace.py [--dir=my_folder] 
                                  --search-regex=<search_regex>
                                  --replace-regex=<replace_regex>
                                  --glob=[glob_pattern]
                                  --dry-run


                                  Every long option such as --search-regex has a corresponding short option, i.e. -s. Run with -h to see all options.



                                  For example, this will flip all dates from 2017-12-31 to 31-12-2017:



                                  python replace.py --glob=myfile.txt 
                                  --search-regex="(d{4})-(d{2})-(d{2})"
                                  --replace-regex="3-2-1"
                                  --dry-run --verbose


                                  import os
                                  import fnmatch
                                  import sys
                                  import shutil
                                  import re

                                  import argparse

                                  def find_replace(cfg):
                                  search_pattern = re.compile(cfg.search_regex)

                                  if cfg.dry_run:
                                  print('THIS IS A DRY RUN -- NO FILES WILL BE CHANGED!')

                                  for path, dirs, files in os.walk(os.path.abspath(cfg.dir)):
                                  for filename in fnmatch.filter(files, cfg.glob):

                                  if cfg.print_parent_folder:
                                  pardir = os.path.normpath(os.path.join(path, '..'))
                                  pardir = os.path.split(pardir)[-1]
                                  print('[%s]' % pardir)
                                  filepath = os.path.join(path, filename)

                                  # backup original file
                                  if cfg.create_backup:
                                  backup_path = filepath + '.bak'

                                  while os.path.exists(backup_path):
                                  backup_path += '.bak'
                                  print('DBG: creating backup', backup_path)
                                  shutil.copyfile(filepath, backup_path)

                                  with open(filepath) as f:
                                  old_text = f.read()

                                  all_matches = search_pattern.findall(old_text)

                                  if all_matches:

                                  print('Found {} matches in file {}'.format(len(all_matches), filename))

                                  new_text = search_pattern.sub(cfg.replace_regex, old_text)

                                  if not cfg.dry_run:
                                  with open(filepath, "w") as f:
                                  print('DBG: replacing in file', filepath)
                                  f.write(new_text)
                                  else:
                                  for idx, matches in enumerate(all_matches):
                                  print("Match #{}: {}".format(idx, matches))

                                  print("NEW TEXT:n{}".format(new_text))

                                  elif cfg.verbose:
                                  print('File {} does not contain search regex "{}"'.format(filename, cfg.search_regex))


                                  if __name__ == '__main__':

                                  parser = argparse.ArgumentParser(description='''DESCRIPTION:
                                  Find and replace recursively from the given folder using regular expressions''',
                                  formatter_class=argparse.RawDescriptionHelpFormatter,
                                  epilog='''USAGE:
                                  {0} -d [my_folder] -s <search_regex> -r <replace_regex> -g [glob_pattern]

                                  '''.format(os.path.basename(sys.argv[0])))

                                  parser.add_argument('--dir', '-d',
                                  help='folder to search in; by default current folder',
                                  default='.')

                                  parser.add_argument('--search-regex', '-s',
                                  help='search regex',
                                  required=True)

                                  parser.add_argument('--replace-regex', '-r',
                                  help='replacement regex',
                                  required=True)

                                  parser.add_argument('--glob', '-g',
                                  help='glob pattern, i.e. *.html',
                                  default="*.*")

                                  parser.add_argument('--dry-run', '-dr',
                                  action='store_true',
                                  help="don't replace anything just show what is going to be done",
                                  default=False)

                                  parser.add_argument('--create-backup', '-b',
                                  action='store_true',
                                  help='Create backup files',
                                  default=False)

                                  parser.add_argument('--verbose', '-v',
                                  action='store_true',
                                  help="Show files which don't match the search regex",
                                  default=False)

                                  parser.add_argument('--print-parent-folder', '-p',
                                  action='store_true',
                                  help="Show the parent info for debug",
                                  default=False)

                                  config = parser.parse_args(sys.argv[1:])

                                  find_replace(config)



                                  Here is an updated version of the script which highlights the search terms and replacements with different colors.







                                  share|improve this answer
















                                  I needed something that would provide a dry-run option and would work recursively with a glob, and after trying to do it with awk and sed I gave up and instead did it in python.



                                  The script searches recursively all files matching a glob pattern (e.g. --glob="*.html") for a regex and replaces with the replacement regex:



                                  find_replace.py [--dir=my_folder] 
                                  --search-regex=<search_regex>
                                  --replace-regex=<replace_regex>
                                  --glob=[glob_pattern]
                                  --dry-run


                                  Every long option such as --search-regex has a corresponding short option, i.e. -s. Run with -h to see all options.



                                  For example, this will flip all dates from 2017-12-31 to 31-12-2017:



                                  python replace.py --glob=myfile.txt 
                                  --search-regex="(d{4})-(d{2})-(d{2})"
                                  --replace-regex="3-2-1"
                                  --dry-run --verbose


                                  import os
                                  import fnmatch
                                  import sys
                                  import shutil
                                  import re

                                  import argparse

                                  def find_replace(cfg):
                                  search_pattern = re.compile(cfg.search_regex)

                                  if cfg.dry_run:
                                  print('THIS IS A DRY RUN -- NO FILES WILL BE CHANGED!')

                                  for path, dirs, files in os.walk(os.path.abspath(cfg.dir)):
                                  for filename in fnmatch.filter(files, cfg.glob):

                                  if cfg.print_parent_folder:
                                  pardir = os.path.normpath(os.path.join(path, '..'))
                                  pardir = os.path.split(pardir)[-1]
                                  print('[%s]' % pardir)
                                  filepath = os.path.join(path, filename)

                                  # backup original file
                                  if cfg.create_backup:
                                  backup_path = filepath + '.bak'

                                  while os.path.exists(backup_path):
                                  backup_path += '.bak'
                                  print('DBG: creating backup', backup_path)
                                  shutil.copyfile(filepath, backup_path)

                                  with open(filepath) as f:
                                  old_text = f.read()

                                  all_matches = search_pattern.findall(old_text)

                                  if all_matches:

                                  print('Found {} matches in file {}'.format(len(all_matches), filename))

                                  new_text = search_pattern.sub(cfg.replace_regex, old_text)

                                  if not cfg.dry_run:
                                  with open(filepath, "w") as f:
                                  print('DBG: replacing in file', filepath)
                                  f.write(new_text)
                                  else:
                                  for idx, matches in enumerate(all_matches):
                                  print("Match #{}: {}".format(idx, matches))

                                  print("NEW TEXT:n{}".format(new_text))

                                  elif cfg.verbose:
                                  print('File {} does not contain search regex "{}"'.format(filename, cfg.search_regex))


                                  if __name__ == '__main__':

                                  parser = argparse.ArgumentParser(description='''DESCRIPTION:
                                  Find and replace recursively from the given folder using regular expressions''',
                                  formatter_class=argparse.RawDescriptionHelpFormatter,
                                  epilog='''USAGE:
                                  {0} -d [my_folder] -s <search_regex> -r <replace_regex> -g [glob_pattern]

                                  '''.format(os.path.basename(sys.argv[0])))

                                  parser.add_argument('--dir', '-d',
                                  help='folder to search in; by default current folder',
                                  default='.')

                                  parser.add_argument('--search-regex', '-s',
                                  help='search regex',
                                  required=True)

                                  parser.add_argument('--replace-regex', '-r',
                                  help='replacement regex',
                                  required=True)

                                  parser.add_argument('--glob', '-g',
                                  help='glob pattern, i.e. *.html',
                                  default="*.*")

                                  parser.add_argument('--dry-run', '-dr',
                                  action='store_true',
                                  help="don't replace anything just show what is going to be done",
                                  default=False)

                                  parser.add_argument('--create-backup', '-b',
                                  action='store_true',
                                  help='Create backup files',
                                  default=False)

                                  parser.add_argument('--verbose', '-v',
                                  action='store_true',
                                  help="Show files which don't match the search regex",
                                  default=False)

                                  parser.add_argument('--print-parent-folder', '-p',
                                  action='store_true',
                                  help="Show the parent info for debug",
                                  default=False)

                                  config = parser.parse_args(sys.argv[1:])

                                  find_replace(config)



                                  Here is an updated version of the script which highlights the search terms and replacements with different colors.








                                  share|improve this answer















                                  share|improve this answer




                                  share|improve this answer



                                  share|improve this answer








                                  edited Jan 7 '18 at 19:31

























                                  answered Nov 15 '17 at 21:59









                                  ccpizzaccpizza

                                  7711 gold badge10 silver badges11 bronze badges




                                  7711 gold badge10 silver badges11 bronze badges











                                  • 1





                                    I don't understand why you would make something this complex. For recursion, use either bash's (or your shell's equivalent) globstar option and ** globs or find. For a dry run, just use sed. Unless you use the -i option, it won't make any changes. For a backup use sed -i.bak (or perl -i .bak); for files that don't match, use grep PATTERN file || echo file. And why in the world would you have python expand the glob instead of letting the shell do it? Why script.py --glob=foo* instead of just script.py foo*?

                                    – terdon
                                    Nov 23 '17 at 9:34








                                  • 1





                                    My why's are very simple: (1) above all, ease of debugging; (2) using only a single well documented tool with a supportive community (3) not knowing sed and awk well and being unwilling to invest extra time on mastering them, (4) readability, (5) this solution will also work on non-posix systems (not that I need that but somebody else might).

                                    – ccpizza
                                    Nov 23 '17 at 12:59
















                                  • 1





                                    I don't understand why you would make something this complex. For recursion, use either bash's (or your shell's equivalent) globstar option and ** globs or find. For a dry run, just use sed. Unless you use the -i option, it won't make any changes. For a backup use sed -i.bak (or perl -i .bak); for files that don't match, use grep PATTERN file || echo file. And why in the world would you have python expand the glob instead of letting the shell do it? Why script.py --glob=foo* instead of just script.py foo*?

                                    – terdon
                                    Nov 23 '17 at 9:34








                                  • 1





                                    My why's are very simple: (1) above all, ease of debugging; (2) using only a single well documented tool with a supportive community (3) not knowing sed and awk well and being unwilling to invest extra time on mastering them, (4) readability, (5) this solution will also work on non-posix systems (not that I need that but somebody else might).

                                    – ccpizza
                                    Nov 23 '17 at 12:59










                                  1




                                  1





                                  I don't understand why you would make something this complex. For recursion, use either bash's (or your shell's equivalent) globstar option and ** globs or find. For a dry run, just use sed. Unless you use the -i option, it won't make any changes. For a backup use sed -i.bak (or perl -i .bak); for files that don't match, use grep PATTERN file || echo file. And why in the world would you have python expand the glob instead of letting the shell do it? Why script.py --glob=foo* instead of just script.py foo*?

                                  – terdon
                                  Nov 23 '17 at 9:34







                                  I don't understand why you would make something this complex. For recursion, use either bash's (or your shell's equivalent) globstar option and ** globs or find. For a dry run, just use sed. Unless you use the -i option, it won't make any changes. For a backup use sed -i.bak (or perl -i .bak); for files that don't match, use grep PATTERN file || echo file. And why in the world would you have python expand the glob instead of letting the shell do it? Why script.py --glob=foo* instead of just script.py foo*?

                                  – terdon
                                  Nov 23 '17 at 9:34






                                  1




                                  1





                                  My why's are very simple: (1) above all, ease of debugging; (2) using only a single well documented tool with a supportive community (3) not knowing sed and awk well and being unwilling to invest extra time on mastering them, (4) readability, (5) this solution will also work on non-posix systems (not that I need that but somebody else might).

                                  – ccpizza
                                  Nov 23 '17 at 12:59







                                  My why's are very simple: (1) above all, ease of debugging; (2) using only a single well documented tool with a supportive community (3) not knowing sed and awk well and being unwilling to invest extra time on mastering them, (4) readability, (5) this solution will also work on non-posix systems (not that I need that but somebody else might).

                                  – ccpizza
                                  Nov 23 '17 at 12:59













                                  0


















                                  ripgrep (command name rg) is a grep tool, but supports search and replace as well.



                                  $ cat ip.txt
                                  dark blue and light blue
                                  light orange
                                  blue sky
                                  $ # by default, line number is displayed if output destination is stdout
                                  $ # by default, only lines that matched the given pattern is displayed
                                  $ # 'blue' is search pattern and -r 'red' is replacement string
                                  $ rg 'blue' -r 'red' ip.txt
                                  1:dark red and light red
                                  3:red sky

                                  $ # --passthru option is useful to print all lines, whether or not it matched
                                  $ # -N will disable line number prefix
                                  $ # this command is similar to: sed 's/blue/red/g' ip.txt
                                  $ rg --passthru -N 'blue' -r 'red' ip.txt
                                  dark red and light red
                                  light orange
                                  red sky



                                  rg doesn't support in-place option, so you'll have to do it yourself



                                  $ # -N isn't needed here as output destination is a file
                                  $ rg --passthru 'blue' -r 'red' ip.txt > tmp.txt && mv tmp.txt ip.txt
                                  $ cat ip.txt
                                  dark red and light red
                                  light orange
                                  red sky




                                  See Rust regex documentation for regular expression syntax and features. The -P switch will enable PCRE2 flavor. rg supports Unicode by default.



                                  $ # non-greedy quantifier is supported
                                  $ echo 'food land bark sand band cue combat' | rg 'foo.*?ba' -r 'X'
                                  Xrk sand band cue combat

                                  $ # unicode support
                                  $ echo 'fox:αλεπού,eagle:αετός' | rg 'p{L}+' -r '($0)'
                                  (fox):(αλεπού),(eagle):(αετός)

                                  $ # set operator example, remove all punctuation characters except . ! and ?
                                  $ para='"Hi", there! How *are* you? All fine here.'
                                  $ echo "$para" | rg '[[:punct:]--[.!?]]+' -r ''
                                  Hi there! How are you? All fine here.

                                  $ # use -P if you need even more advanced features
                                  $ echo 'car bat cod map' | rg -P '(bat|map)(*SKIP)(*F)|w+' -r '[$0]'
                                  [car] bat [cod] map




                                  Like grep, the -F option will allow fixed strings to be matched, a handy option which I feel sed should implement too.



                                  $ printf '2.3/[4]*6nfoon5.3-[4]*9n' | rg --passthru -F '[4]*' -r '2'
                                  2.3/26
                                  foo
                                  5.3-29




                                  Another handy option is -U which enables multiline matching



                                  $ # (?s) flag will allow . to match newline characters as well
                                  $ printf '42nHi therenHave a Nice Day' | rg --passthru -U '(?s)the.*ice' -r ''
                                  42
                                  Hi Day



                                  rg can handle dos-style files too



                                  $ # same as: sed -E 's/w+(r?)$/1231/'
                                  $ printf 'hi thererngood dayrn' | rg --passthru --crlf 'w+$' -r '123'
                                  hi 123
                                  good 123




                                  Another advantage of rg is that it is likely to be faster than sed



                                  $ # for small files, initial processing time of rg is a large component
                                  $ time echo 'aba' | sed 's/a/b/g' > f1
                                  real 0m0.002s
                                  $ time echo 'aba' | rg --passthru 'a' -r 'b' > f2
                                  real 0m0.007s

                                  $ # for larger files, rg is likely to be faster
                                  $ # 6.2M sample ASCII file
                                  $ wget https://norvig.com/big.txt
                                  $ time LC_ALL=C sed 's/bcatb/dog/g' big.txt > f1
                                  real 0m0.060s
                                  $ time rg --passthru 'bcatb' -r 'dog' big.txt > f2
                                  real 0m0.048s
                                  $ diff -s f1 f2
                                  Files f1 and f2 are identical

                                  $ time LC_ALL=C sed -E 's/b(w+)(s+1)+b/1/g' big.txt > f1
                                  real 0m0.725s
                                  $ time rg --no-pcre2-unicode --passthru -wP '(w+)(s+1)+' -r '$1' big.txt > f2
                                  real 0m0.093s
                                  $ diff -s f1 f2
                                  Files f1 and f2 are identical





                                  share|improve this answer































                                    0


















                                    ripgrep (command name rg) is a grep tool, but supports search and replace as well.



                                    $ cat ip.txt
                                    dark blue and light blue
                                    light orange
                                    blue sky
                                    $ # by default, line number is displayed if output destination is stdout
                                    $ # by default, only lines that matched the given pattern is displayed
                                    $ # 'blue' is search pattern and -r 'red' is replacement string
                                    $ rg 'blue' -r 'red' ip.txt
                                    1:dark red and light red
                                    3:red sky

                                    $ # --passthru option is useful to print all lines, whether or not it matched
                                    $ # -N will disable line number prefix
                                    $ # this command is similar to: sed 's/blue/red/g' ip.txt
                                    $ rg --passthru -N 'blue' -r 'red' ip.txt
                                    dark red and light red
                                    light orange
                                    red sky



                                    rg doesn't support in-place option, so you'll have to do it yourself



                                    $ # -N isn't needed here as output destination is a file
                                    $ rg --passthru 'blue' -r 'red' ip.txt > tmp.txt && mv tmp.txt ip.txt
                                    $ cat ip.txt
                                    dark red and light red
                                    light orange
                                    red sky




                                    See Rust regex documentation for regular expression syntax and features. The -P switch will enable PCRE2 flavor. rg supports Unicode by default.



                                    $ # non-greedy quantifier is supported
                                    $ echo 'food land bark sand band cue combat' | rg 'foo.*?ba' -r 'X'
                                    Xrk sand band cue combat

                                    $ # unicode support
                                    $ echo 'fox:αλεπού,eagle:αετός' | rg 'p{L}+' -r '($0)'
                                    (fox):(αλεπού),(eagle):(αετός)

                                    $ # set operator example, remove all punctuation characters except . ! and ?
                                    $ para='"Hi", there! How *are* you? All fine here.'
                                    $ echo "$para" | rg '[[:punct:]--[.!?]]+' -r ''
                                    Hi there! How are you? All fine here.

                                    $ # use -P if you need even more advanced features
                                    $ echo 'car bat cod map' | rg -P '(bat|map)(*SKIP)(*F)|w+' -r '[$0]'
                                    [car] bat [cod] map




                                    Like grep, the -F option will allow fixed strings to be matched, a handy option which I feel sed should implement too.



                                    $ printf '2.3/[4]*6nfoon5.3-[4]*9n' | rg --passthru -F '[4]*' -r '2'
                                    2.3/26
                                    foo
                                    5.3-29




                                    Another handy option is -U which enables multiline matching



                                    $ # (?s) flag will allow . to match newline characters as well
                                    $ printf '42nHi therenHave a Nice Day' | rg --passthru -U '(?s)the.*ice' -r ''
                                    42
                                    Hi Day



                                    rg can handle dos-style files too



                                    $ # same as: sed -E 's/w+(r?)$/1231/'
                                    $ printf 'hi thererngood dayrn' | rg --passthru --crlf 'w+$' -r '123'
                                    hi 123
                                    good 123




                                    Another advantage of rg is that it is likely to be faster than sed



                                    $ # for small files, initial processing time of rg is a large component
                                    $ time echo 'aba' | sed 's/a/b/g' > f1
                                    real 0m0.002s
                                    $ time echo 'aba' | rg --passthru 'a' -r 'b' > f2
                                    real 0m0.007s

                                    $ # for larger files, rg is likely to be faster
                                    $ # 6.2M sample ASCII file
                                    $ wget https://norvig.com/big.txt
                                    $ time LC_ALL=C sed 's/bcatb/dog/g' big.txt > f1
                                    real 0m0.060s
                                    $ time rg --passthru 'bcatb' -r 'dog' big.txt > f2
                                    real 0m0.048s
                                    $ diff -s f1 f2
                                    Files f1 and f2 are identical

                                    $ time LC_ALL=C sed -E 's/b(w+)(s+1)+b/1/g' big.txt > f1
                                    real 0m0.725s
                                    $ time rg --no-pcre2-unicode --passthru -wP '(w+)(s+1)+' -r '$1' big.txt > f2
                                    real 0m0.093s
                                    $ diff -s f1 f2
                                    Files f1 and f2 are identical





                                    share|improve this answer





























                                      0














                                      0










                                      0









                                      ripgrep (command name rg) is a grep tool, but supports search and replace as well.



                                      $ cat ip.txt
                                      dark blue and light blue
                                      light orange
                                      blue sky
                                      $ # by default, line number is displayed if output destination is stdout
                                      $ # by default, only lines that matched the given pattern is displayed
                                      $ # 'blue' is search pattern and -r 'red' is replacement string
                                      $ rg 'blue' -r 'red' ip.txt
                                      1:dark red and light red
                                      3:red sky

                                      $ # --passthru option is useful to print all lines, whether or not it matched
                                      $ # -N will disable line number prefix
                                      $ # this command is similar to: sed 's/blue/red/g' ip.txt
                                      $ rg --passthru -N 'blue' -r 'red' ip.txt
                                      dark red and light red
                                      light orange
                                      red sky



                                      rg doesn't support in-place option, so you'll have to do it yourself



                                      $ # -N isn't needed here as output destination is a file
                                      $ rg --passthru 'blue' -r 'red' ip.txt > tmp.txt && mv tmp.txt ip.txt
                                      $ cat ip.txt
                                      dark red and light red
                                      light orange
                                      red sky




                                      See Rust regex documentation for regular expression syntax and features. The -P switch will enable PCRE2 flavor. rg supports Unicode by default.



                                      $ # non-greedy quantifier is supported
                                      $ echo 'food land bark sand band cue combat' | rg 'foo.*?ba' -r 'X'
                                      Xrk sand band cue combat

                                      $ # unicode support
                                      $ echo 'fox:αλεπού,eagle:αετός' | rg 'p{L}+' -r '($0)'
                                      (fox):(αλεπού),(eagle):(αετός)

                                      $ # set operator example, remove all punctuation characters except . ! and ?
                                      $ para='"Hi", there! How *are* you? All fine here.'
                                      $ echo "$para" | rg '[[:punct:]--[.!?]]+' -r ''
                                      Hi there! How are you? All fine here.

                                      $ # use -P if you need even more advanced features
                                      $ echo 'car bat cod map' | rg -P '(bat|map)(*SKIP)(*F)|w+' -r '[$0]'
                                      [car] bat [cod] map




                                      Like grep, the -F option will allow fixed strings to be matched, a handy option which I feel sed should implement too.



                                      $ printf '2.3/[4]*6nfoon5.3-[4]*9n' | rg --passthru -F '[4]*' -r '2'
                                      2.3/26
                                      foo
                                      5.3-29




                                      Another handy option is -U which enables multiline matching



                                      $ # (?s) flag will allow . to match newline characters as well
                                      $ printf '42nHi therenHave a Nice Day' | rg --passthru -U '(?s)the.*ice' -r ''
                                      42
                                      Hi Day



                                      rg can handle dos-style files too



                                      $ # same as: sed -E 's/w+(r?)$/1231/'
                                      $ printf 'hi thererngood dayrn' | rg --passthru --crlf 'w+$' -r '123'
                                      hi 123
                                      good 123




                                      Another advantage of rg is that it is likely to be faster than sed



                                      $ # for small files, initial processing time of rg is a large component
                                      $ time echo 'aba' | sed 's/a/b/g' > f1
                                      real 0m0.002s
                                      $ time echo 'aba' | rg --passthru 'a' -r 'b' > f2
                                      real 0m0.007s

                                      $ # for larger files, rg is likely to be faster
                                      $ # 6.2M sample ASCII file
                                      $ wget https://norvig.com/big.txt
                                      $ time LC_ALL=C sed 's/bcatb/dog/g' big.txt > f1
                                      real 0m0.060s
                                      $ time rg --passthru 'bcatb' -r 'dog' big.txt > f2
                                      real 0m0.048s
                                      $ diff -s f1 f2
                                      Files f1 and f2 are identical

                                      $ time LC_ALL=C sed -E 's/b(w+)(s+1)+b/1/g' big.txt > f1
                                      real 0m0.725s
                                      $ time rg --no-pcre2-unicode --passthru -wP '(w+)(s+1)+' -r '$1' big.txt > f2
                                      real 0m0.093s
                                      $ diff -s f1 f2
                                      Files f1 and f2 are identical





                                      share|improve this answer














                                      ripgrep (command name rg) is a grep tool, but supports search and replace as well.



                                      $ cat ip.txt
                                      dark blue and light blue
                                      light orange
                                      blue sky
                                      $ # by default, line number is displayed if output destination is stdout
                                      $ # by default, only lines that matched the given pattern is displayed
                                      $ # 'blue' is search pattern and -r 'red' is replacement string
                                      $ rg 'blue' -r 'red' ip.txt
                                      1:dark red and light red
                                      3:red sky

                                      $ # --passthru option is useful to print all lines, whether or not it matched
                                      $ # -N will disable line number prefix
                                      $ # this command is similar to: sed 's/blue/red/g' ip.txt
                                      $ rg --passthru -N 'blue' -r 'red' ip.txt
                                      dark red and light red
                                      light orange
                                      red sky



                                      rg doesn't support in-place option, so you'll have to do it yourself



                                      $ # -N isn't needed here as output destination is a file
                                      $ rg --passthru 'blue' -r 'red' ip.txt > tmp.txt && mv tmp.txt ip.txt
                                      $ cat ip.txt
                                      dark red and light red
                                      light orange
                                      red sky




                                      See Rust regex documentation for regular expression syntax and features. The -P switch will enable PCRE2 flavor. rg supports Unicode by default.



                                      $ # non-greedy quantifier is supported
                                      $ echo 'food land bark sand band cue combat' | rg 'foo.*?ba' -r 'X'
                                      Xrk sand band cue combat

                                      $ # unicode support
                                      $ echo 'fox:αλεπού,eagle:αετός' | rg 'p{L}+' -r '($0)'
                                      (fox):(αλεπού),(eagle):(αετός)

                                      $ # set operator example, remove all punctuation characters except . ! and ?
                                      $ para='"Hi", there! How *are* you? All fine here.'
                                      $ echo "$para" | rg '[[:punct:]--[.!?]]+' -r ''
                                      Hi there! How are you? All fine here.

                                      $ # use -P if you need even more advanced features
                                      $ echo 'car bat cod map' | rg -P '(bat|map)(*SKIP)(*F)|w+' -r '[$0]'
                                      [car] bat [cod] map




                                      Like grep, the -F option will allow fixed strings to be matched, a handy option which I feel sed should implement too.



                                      $ printf '2.3/[4]*6nfoon5.3-[4]*9n' | rg --passthru -F '[4]*' -r '2'
                                      2.3/26
                                      foo
                                      5.3-29




                                      Another handy option is -U which enables multiline matching



                                      $ # (?s) flag will allow . to match newline characters as well
                                      $ printf '42nHi therenHave a Nice Day' | rg --passthru -U '(?s)the.*ice' -r ''
                                      42
                                      Hi Day



                                      rg can handle dos-style files too



                                      $ # same as: sed -E 's/w+(r?)$/1231/'
                                      $ printf 'hi thererngood dayrn' | rg --passthru --crlf 'w+$' -r '123'
                                      hi 123
                                      good 123




                                      Another advantage of rg is that it is likely to be faster than sed



                                      $ # for small files, initial processing time of rg is a large component
                                      $ time echo 'aba' | sed 's/a/b/g' > f1
                                      real 0m0.002s
                                      $ time echo 'aba' | rg --passthru 'a' -r 'b' > f2
                                      real 0m0.007s

                                      $ # for larger files, rg is likely to be faster
                                      $ # 6.2M sample ASCII file
                                      $ wget https://norvig.com/big.txt
                                      $ time LC_ALL=C sed 's/bcatb/dog/g' big.txt > f1
                                      real 0m0.060s
                                      $ time rg --passthru 'bcatb' -r 'dog' big.txt > f2
                                      real 0m0.048s
                                      $ diff -s f1 f2
                                      Files f1 and f2 are identical

                                      $ time LC_ALL=C sed -E 's/b(w+)(s+1)+b/1/g' big.txt > f1
                                      real 0m0.725s
                                      $ time rg --no-pcre2-unicode --passthru -wP '(w+)(s+1)+' -r '$1' big.txt > f2
                                      real 0m0.093s
                                      $ diff -s f1 f2
                                      Files f1 and f2 are identical






                                      share|improve this answer













                                      share|improve this answer




                                      share|improve this answer



                                      share|improve this answer










                                      answered 53 mins ago









                                      SundeepSundeep

                                      7,9561 gold badge13 silver badges30 bronze badges




                                      7,9561 gold badge13 silver badges30 bronze badges




















                                          protected by Community Jul 27 '15 at 21:05



                                          Thank you for your interest in this question.
                                          Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).



                                          Would you like to answer one of these unanswered questions instead?



                                          Popular posts from this blog

                                          Taj Mahal Inhaltsverzeichnis Aufbau | Geschichte | 350-Jahr-Feier | Heutige Bedeutung | Siehe auch |...

                                          Baia Sprie Cuprins Etimologie | Istorie | Demografie | Politică și administrație | Arii naturale...

                                          Nicolae Petrescu-Găină Cuprins Biografie | Opera | In memoriam | Varia | Controverse, incertitudini...