Is it possible to substitute a sample recursively in multi directory with grep and sed like that ?

grep -e "http://localhost:4000" -r ~/dev_web/_site  | sed -e 's/http:\/\/localhost:4000/https:\/\/another_site.com/g'

When I do that, my terminal show me that is done, but when I do again a grep:

grep -e "https://another_site.com" -r ~/dev_web/_site

it show me only 4 files changed..

And if I do :

 grep -e "http://localhost:4000" -r ~/dev_web/_site/

the sample http:localhost:4000 is still here


Your pipeline changes no files. It pulls out lines that matches the regular expression using grep, then changes that data, but does not attempt to make changes in any files.

Instead, assuming GNU sed:

find ~/dev_web/_site -type f \
    -exec grep -qF 'http://localhost:4000' {} ';' \
    -exec sed -i 's#http://localhost:4000#https://another.site#g' {} +

This would find all regular files in or under the directory ~/dev_web/_site. For each such file, grep first tests whether the file contains the given string. If it does, GNU sed is invoked to make an in-place substitution.

The grep step could be omitted, but GNU sed would update the modification timestamp on all files without it.

You may want to only look in a subset of the files in the directories beneath ~/dev_web/_site. If this is the case, restrict the search with something like -name '*.js' (or whatever name pattern you want to match) just after -type f.

Adding -print just before -exec sed ... would print the pathnames of the files that find would give to sed.



your sed session worked on grep's output, not actual file.

use grep to list file (*)

grep -e "http://localhost:4000" -r ~/dev_web/_site -l 

then use xargs and sed -i to edit in place

xargs sed -i -e 's;http://localhost:4000;https://another_site.com;g'

(on a side note you can change delimiter in sed)

Now all together

grep -e "http://localhost:4000" -r ~/dev_web/_site -l |
xargs sed -i -e 's;http://localhost:4000;https://another_site.com;g'

This can be one-lined of course, note that pipe symbol (|) can be use a line continuation.

(*) I assume file don't have space, new line and other funny char in their names.

  • Thanks l didn't saw your answer yet – ordinatous Aug 28 '20 at 8:15

Your first grep extract lines from files, not list files.
To obtain a list of files matched use the -l option to grep:

$ grep -rl "htt...."  ~/dir

To execute the modification of files with sed, give the list to sed (invert the order):

$ sed -e 's#http://localhost:4000#https://another_site.com#g' \
$(grep -rle "http://localhost:4000" ~/dev_web/_site)

That assume "clean" filenames: No spaces or new lines.

A more robust option is to use find:

find "$searchdir" -type f \
    -exec grep -qF "$fromname" {} ';' \
    -exec sed -i 's#'"$fromname"'#'"$toname"'#g' {} +

The best for me is with a for :

for i in $(grep -R "string_to_change" | awk -F: '{print $1}');do sed -i 's/string_to_change/string_changed/g' $i ;done

The explanation: with grep -R lookfor the string recursively, always put the name with a : before, so you have the hole path of the file. And finally do the sed to the file extracted.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.