Shell script to replace string in multiple files within selected directories

filesscripting

I created the script below which takes the path of a single directory and replaces search string in all the files within that directory. I would like to enhance this script in such a way that it can search and replace the string in multiple directories which are listed in an external input file.

External input file content:

/var/start/system1/dir1
/var/start/system2/dir2
/var/start/system3/dir3
/var/start/system4/dir4

Script with one directory:

filepath="/var/start/system/dir1"
searchstring="test"
replacestring="test01"

i=0; 

for file in $(grep -l -R $searchstring $filepath)
do
  cp $file $file.bak
  sed -e "s/$searchstring/$replacestring/ig" $file > tempfile.tmp
  mv tempfile.tmp $file

  let i++;

  echo "Modified: " $file
done

Best Answer

First of all, the tmpfile dance can be avoided by using sed -i with GNU sed or sed -i '' with FreeBSD's (in-place replacement).

grep -R can take multiple paths on the command line, so if you are confident that none of the paths contain spaces, you can replace $(grep -l -Re "$searchstring" "$filepath") with $(grep -l -R "$searchstring" $(cat path_list)).

This will fail if any of the paths contain spaces, tabs, or any globbing character, but so will the original.

A much more robust approach uses find and just applies sed to all of the files, trusting it not to modify files with no matches (here assuming a bash shell):

# Read newline-separated path list into array from file 'path_list'
IFS=$'\n' read -d '' -r -a paths path_list

# Run sed on everything
find "${paths[@]}" \
  -exec sed -i -r -e "s/$searchstring/$replacestring/ig" '{}' ';'

But this doesn't give you any feedback on which files it's modifying.

A lengthier version that does give you the feedback:

# Read newline-separated path list into array from file 'path_list'
IFS=$'\n' read -d '' -r -a paths path_list

grep -l -R "$searchstring" "${paths[@]}" | while IFS= read -r file; do
  sed -r -i -e "s/$searchstring/$replacestring/ig" "$file"
  echo "Modified: $file"
done
Related Question