Question

I'm trying to write a script that basically works like Microsoft words find>replace function. It first asks for two inputs from the user. First one for strings to be found and the second one for strings to replace the old ones. Even though this is pretty straight forward, I also want to count the number of things to be replaced and echo the user to confirms these specific number of replacements. How can I do that? So far I have this as the search>replace function:

for file in `find -name '*.sdf.new'`;  
do  
    grep "$search" $file &> /dev/null  
    sed -i "s/$search/$replace/" $file  
done
Was it helpful?

Solution

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.
  • 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.

OTHER TIPS

check_val=`grep "$search" $file`
if [[ "x$check_val" != "x" ]]
then
        read -p "replace ?" response;
        if [[ "$response" == "y" ]]
        then
                sed -i "s/$search/$replace/" $file
        fi
fi
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top