while read -u 3 -r -d '' file; do
n=$(grep -o "$search" "$file" | wc -l)
read -p "About to replace $n instances of '$search' in $file. OK? " ans
if [[ $ans == [yY]* ]]; then
sed -i "s|${search//|/\\\\|}|${replace//|/\\\\|}|g" "$file"
fi
done 3< <(find -name '*.sdf.new' -print0)
There's some tricky stuff going on here:
- the output from the find command is send to the while loop on file descriptor 3, and read uses that fd to grab filenames
- this is necessary because there is a read command inside the loop to interact with the user that has to use stdin.
- the while loop reads from a process substitution
<(find -name ...)
so that no subshell has to be created and to facilitate the use of a different file descriptor.- in bash, when you say
cmd1 | cmd2
, new bash processes are created for each side of the pipe. This can cause problems in that variables assigned in a subshell are not present in the current shell, not the case here.
- in bash, when you say
- to properly handle files with weird names, I use the GNU find
-print0
feature which separates the filenames with a zero byte, and then use read's-d ''
option. grep -c
counts the number of lines with a match.grep -o pattern file | wc -l
counts the number of actual matches.- the crazy sed script adds protection in case the search or the replacement strings contain the
s
commands delimiter. I use|
instead of/
because the escaping got even more extreme.