0

I'm trying to create a shell search and replace function to replace all occurrences of a string in a directory. The problem is how do I take the output of recursive grep and use it to find files to use for sed?

I've got the following pipeline grep -R protal ./ | sed 's/:.*//g' | sed 's/\/\//\//g'. It produces this output:

./settings.py
./settings.py
./settings.py
./urls.py
./wsgi.py
./wsgi.py

I want to take this and split it into an array or something so I can do (pseudo-code):

for [[ $file in $file_list ]]; do
   sed -i 's/$input_string/$replace_value/g' $file
done

How would I do that?

Jonathan
  • 539
  • 4
  • 15

3 Answers3

2

Instead of trying to parse a file list (which could be a problem when you have whitespaces or newlines in file names), I'd do the whole thing with find and its -exec filter:

find . -type f -exec grep -q protal '{}' \; -exec sed -i "s/$input_string/$replace_value/" '{}' \;

The trick is that

  • the -exec filter only passes if the command returns with exit status 0,
  • that grep -q protal file.txt returns with exit status 0 only if file.txt contains protal, and that
  • the second -exec filter, since it is chained after the first, will only be attempted (and the associated command only executed) if the first filter passes.

This has the effect of running sed -i "s/$input_string/$replace_value/" for regular files under the current directory that contain protal.

Wintermute
  • 42,983
  • 5
  • 77
  • 80
  • This is the only sane answer. – gniourf_gniourf Apr 29 '15 at 17:37
  • Ok, so I like this answer, and it's certainly better than my original idea, however, I'm getting an error. Sed returns `sed: 1: "./file": invalid command code .` What's up with that? – Jonathan Apr 29 '15 at 19:09
  • Does one of the shell variables contain a slash? If so, see https://stackoverflow.com/questions/5864146/use-slashes-in-sed-replace – Wintermute Apr 29 '15 at 20:08
  • Another wild guess: If you're using Mac OS X or a BSD, those come with BSD sed, which expects a filename extension for the backup file as parameter to `-i`. It would treat `s/.../.../` as the filename extension and the filename as sed code. I believe it allows an empty extension to avoid the generation of a backup file, so you could try `-exec sed -i '' "s/$input_string/$replace_string/" '{}' \;`; if that doesn't work, `-exec sed -i.bak "s/$input_string/$replace_string/" '{}' \; -exec rm '{}.bak' \;` will do the trick (if no `foo.bak` files that need to be preserved are in the directory tree). – Wintermute Apr 30 '15 at 07:01
0
for file in $(grep -R protal ./ | sed 's/:.*//g' | sed 's/\/\//\//g'); do
#yourcode
done
Jahid
  • 21,542
  • 10
  • 90
  • 108
0

Just use xargs :

grep -R protal ./ | sed 's/:.*//g' | sed 's/\/\//\//g' | xargs  sed -i 's/$input_string/$replace_value/g'
Tristan Foureur
  • 1,647
  • 10
  • 23