function sr() {
declare pattern replacement name usage
declare -i pvar=0 rvar=0 tvar=0
usage='usage: sr [-t ] [-n name] [-p pattern] [-r replacement] [-- ] [dir1 dir2 ...]'
# cf. http://bsdpants.blogspot.com/2007/02/option-ize-your-shell-scripts.html
while [[ "${1:0:1}" == '-' ]] ; do
[[ "${1}" == '--' ]] && { shift; break; } # -- marks end of options
[[ ${#1} -ne 2 ]] && { echo "${usage}"; return 1; }
case "${1:1:1}" in
'n') name="${2}"
shift 2 ;;
'p') pattern="${2}"
pvar=1
shift 2 ;;
'r') replacement="${2}"
rvar=1
shift 2 ;;
't') tvar=1
shift ;;
*) echo "${usage}"
return 1 ;;
esac
done
if [[ $pvar -eq 0 ]] || [[ $rvar -eq 0 ]]; then
echo 'Both options -p and -r must be specified!'
echo "${usage}"
return 1
fi
while read -d $'\0' fpath; do
if [[ -z "$(/usr/bin/egrep -I -m1 '^.' "${fpath}")" ]]; then # skip binary files
echo "Binary file: ${fpath}"
continue
fi
if [[ $tvar -eq 0 ]]; then # perform an actual search & replace
cat <<-EOF | /bin/ed -s "${fpath}"
H
g/${pattern}/s/${pattern}/${replacement}/g
wq
EOF
else # only simulate a search & replace and print the result to stdout
printf "\n\n\033[1m%s\033[m\n\n\n" "${fpath}" # print the current file path
# delete all lines that do not contain a pattern match: v/...
# add a final trailing new line in case all lines got deleted: \$a...
# do the matching: g/${pattern}/...
# insert '#: ' at the beginning of each line: g/./s/\(...
cat <<-EOF | /bin/ed -s "${fpath}"
H
v/${pattern}/d
\$a
.
g/${pattern}/s/${pattern}/$(printf "\033[32m${replacement}\033[m")/g
g/./s/\(.*\)/#: \1/g
,p
EOF
fi
done < <(/usr/bin/find -x "${@}" -type f -name "*${name}" -not -empty -print0)
}
man ed | less -p 'REGULAR EXPRESSIONS'
# test mode
sr -t -p hello -r world -n ".txt" /path/to/dir | less -r
# actual search & replace without previous backup
sr -p hello -r world -n ".txt" /path/to/dir
# backslash-escape the / character
sr -t -p '\/usr\/local' -r '\/opt\/local' /path/to/file
Read more: http://feeds.dzone.com/~r/dzone/snippets/~3/IOT5cMjpLjA/10695