#!/bin/bash -norc # regexp replace a string by another in a files (inplace!) # ... online at http://www.jjj.de/ # author: Joerg Arndt ( arndt (AT) jjj.de ) # needs perl # version given in function usage() set - ######################################## THIS='replace'; # `basename $0`; ## DO NOT copy and paste the examples from this file, ## instead copy from the output on your terminal ! ## or do a 'replace --help > replace.txt' and copy from there function usage { cat <" produces a diff-file "-D " _only_ produces a diff-file "-e ''" auto-Escape chars from in OLDEXPR (slashes are always escaped) "-o " do not work inplace: use output file this option can only be used with exactly one file "-i" ignore case in OLDEXPR "-m" allow multiline match "-N" don't error if no files given (useful for scripts) Examples: $THIS old_func_name new_func_name *.cc ## you figure this out $THIS '([^ ]+) +$' '$1' *.txt ## remove trailing spaces $THIS -n -c oldstr newstr foo.txt ## check only, with context $THIS '\r' '\n' mac.txt ## apple line ends (^M) --> unix line ends (\n) $THIS '^ ' '\t' Makefile ## tabify Makefile $THIS '\t' ' ' \$(find mytexts/ -name \*.txt) ## untabify files ## word boundaries: "n" --> "N", but not "and" --> "aNd": $THIS '\bn\b' 'N' *.cc ## ignore case: "Sand, sand, SAND" --> "sane": $THIS -i '\bsand\b' 'sane' *.txt $THIS '//(.*)$' '/\*\$1\*/' *.cc ## C++ comments --> C comments $THIS '/\*(.*)\*/$' '//\$1' *.c ## C comments --> C++ comments ## "sincos(arg1,arg2,arg3)" --> "sincos(arg3, arg2, arg1)": $THIS 'sincos\(([^,]+), *([^,]+), *([^\)]+)\)' \\ 'sincos(\$3, \$2, \$1)' \$(find . -name \*.cc) ## "name_foo" --> "new_foo", "name_bar" --> "new_bar" etc.: $THIS 'name_([a-z]+)' 'new_\$1' *.cc ## replace "jjnote" by "myremark" in all *.cc files under fxt/: ## and produce patchfile fxt.dif: $THIS -d fxt.dif jjnote myremark \$(find fxt/ -name \*.cc) ## ... undo changes by: patch -R -p0 < fxt.dif ## produce patchfile fxt.dif but do NOT change files inside fxt/: $THIS -f -D fxt.dif jjnote myremark \$(find fxt/ -name \*.cc) ## ... apply these paches with: patch -p0 < fxt.dif ## replace "foo" --> "bar" in *.cc in the directory fxt/ ## but only in those files that contain "unixmagic": $THIS foo bar \$(grep -l unixmagic \$(find fxt/ -name \*.cc)) ## tricky: replace comments in postscript files: ## i.e. remove all '% blah' but not '%% blah' & don't touch magic ## (see man perlre 'zero-width negative lookbehind assertion') $THIS -fq '(?' '' \$(find xml -name \*.xml) ## remove XML tag pairs with everything in between, ## even over multiple lines: replace -f -d d2.diff -m '.*?<\/remark>' '' \$(find xml -name \*.xml) $THIS is online: at http://www.jjj.de/ ---------------- author: Joerg Arndt ( arndt (AT) jjj.de ) ---------------- EOF } ## DO NOT copy and paste the examples from this file, ## instead copy from the output on your terminal ! ## or do a 'replace --help > replace.txt' and copy from there if [ "$1" = "--help" ]; then usage; exit 0; fi if [ "$1" = "-help" ]; then usage; exit 0; fi type perl &>/dev/null || { echo "$THIS needs perl ! (exiting)"; exit 1; } ######################################## DUMMY=0; INTERACTIVE=1; QUIET=0; FQUIET=0; DIFFFILENAME=""; DELIM='/'; IGNCASE=''; MULTILINE=''; ESCSET=''; ERRIFNOFILES=1; unset DIFFONLY; unset DIFFOPT; unset OUTFILE; while getopts :h\?fimqQnNcd:D:e:o: OPT; do case $OPT in h|+h|\?|+\?) usage exit 0; ;; f|+f) INTERACTIVE=0; ;; i|+i) IGNCASE='i'; ;; m|+m) MULTILINE='ms'; ;; q|+q) QUIET=1; ;; Q|+Q) FQUIET=1; QUIET=1; ;; n|+n) DUMMY=1; ;; c|+c) DIFFOPT='-C 2' ;; d|+d) DIFFFILENAME=$OPTARG; # echo "diff file = $DIFFFILENAME"; ;; D|+D) DIFFFILENAME=$OPTARG; DIFFONLY=1; # echo "diff file = $DIFFFILENAME, no changes will be applied"; ;; e|+e) ESCSET=$OPTARG; ;; o|+o) OUTFILE=$OPTARG; ;; N|+N) ERRIFNOFILES=0; ;; *) usage; exit 2 esac done if [ -n "$DIFFFILENAME" ]; then # want diff if [ -e "$DIFFFILENAME" ]; then # file exists echo "file $DIFFFILENAME exists, diff will be appended !" if [ -w "$DIFFFILENAME" ]; then # is writable true; # ok else echo "cannot open file $DIFFFILENAME"; exit 1; fi fi echo "# $0 $@" >> "$DIFFFILENAME"; fi shift $[ $OPTIND - 1 ] if [ "$INTERACTIVE" = "1" ]; then QUIET=0; fi if [ "$DUMMY" = "1" ]; then INTERACTIVE=0; fi #echo "1=[$1] 2=[$2] 3=[$3]" # debug if [ $# -lt 3 ]; then if [ "$ERRIFNOFILES" = "1" ]; then usage; exit -1; else if [ $# -lt 2 ]; then usage; exit -1; fi fi fi if [ -n "$OUTFILE" ]; then if [ "$OUTFILE" != '-' ]; then if [ $# -gt 3 ]; then echo "can't use '-o' with multiple filenames. (exiting)"; exit -1; fi fi fi #OLDEXPR=$1; #NEWEXPR=$2; OLDEXPR=${1//\//\\\/}; ## escape slashes NEWEXPR=${2//\//\\\/}; ## escape slashes shift; shift; while [ -n "$ESCSET" ]; do ESCCHAR=${ESCSET:0:1}; ESCSET=${ESCSET:1}; OLDEXPR=${OLDEXPR//\\${ESCCHAR}/\\${ESCCHAR}}; done if [ -z "$OLDEXPR" ]; then echo 'empty OLDEXPR !'; echo ' (exiting) '; exit -1; fi REPTMP=/tmp/replace-$$.tmp REPDIFF=/tmp/replace-$$.diff; #REPBAK=/tmp/replace-$$.bak trap "rm -f $REPTMP $REPDIFF 2>/dev/null" 0 #LINE1="\$cmd='s/$OLDEXPR/$NEWEXPR/${IGNCASE}g'; "; LINE1="\$cmd='s$DELIM$OLDEXPR$DELIM$NEWEXPR${DELIM}${IGNCASE}${MULTILINE}g'; "; LINE2="while() {eval(\$cmd); print;}"; #echo "LINE1=[[$LINE1]]"; # exit 0; PERLCMD="$LINE1 $LINE2"; #echo "PERLCMD=[[$PERLCMD]]"; ## debug ASK='accept these changes ("y" for ok'; if [ $# -gt 1 ]; then ASK=$ASK', "PROCEED" to rush ahead) ?'; else ASK=$ASK') ?'; fi #echo "ASK=[[$ASK]]"; ######################################## for FILE in $*; do # echo "[$FQUIET]" if [ "$FQUIET" = "0" ]; then echo " FILE $FILE : "; fi ## file exists and a regular file ?: if [ ! -f $FILE ]; then echo "skipping $FILE (does not exist or is no regular file)" 1>&2 ; continue; fi ## skip symbolic links: if [ -L $FILE ]; then echo "skipping $FILE (symbolic link)" 1>&2 ; continue; fi ## skip directories: if [ -d $FILE ]; then echo "skipping $FILE (directory)" 1>&2 ; continue; fi ## must be readable: if [ ! -r $FILE ]; then echo "skipping $FILE (not readable)" 1>&2 ; continue; fi ## must be writable: if [ ! -w $FILE ]; then echo "skipping $FILE (not writeable)" 1>&2 ; continue; fi ## skip zero byte files: if [ ! -s $FILE ]; then echo "skipping $FILE (zero length)" 1>&2 ; continue; fi # cp $FILE $REPBAK ## create backup cp $FILE $REPTMP; ## to have identical permissions for new file ############################ ## work is done here: if [ '' = "${MULTILINE}" ]; then perl -we "$PERLCMD" < $FILE > $REPTMP; else perl -0777 -we "$PERLCMD" < $FILE > $REPTMP; fi ############################ ## created zero byte file ? ## most likely an error in the regular expressions if [ ! -s $REPTMP ]; then echo ' ! *** ZERO OUTPUT FILESIZE *** ! ' 1>&2 ; echo ' ? an error in your regexp ?' 1>&2 ; echo ' Switching to interactive mode. You might want to interrupt.' 1>&2 ; INTERACTIVE=1; fi if ! diff $DIFFOPT $FILE $REPTMP > $REPDIFF; then ## if there is a difference ... if [ "$QUIET" = "0" ]; then cat $REPDIFF; fi if [ "$INTERACTIVE" = "1" ]; then read -p "[$FILE] $ASK " ANSWER; if [ "$ANSWER" = "PROCEED" ]; then INTERACTIVE=0; fi else ANSWER=y; fi if [ "$DUMMY" = "0" ]; then ## ... and we are not in dummy mode ... if [ "$ANSWER" = "y" -o "$ANSWER" = "PROCEED" ]; then ## ... and got confirmation echo "changes accepted." test -n "$DIFFFILENAME" && diff -u $FILE $REPTMP >> $DIFFFILENAME; ## ... and DIFFONLY is not empty ... test -n "$OUTFILE" && FILE=$OUTFILE if [ -z "$DIFFONLY" ]; then ## DIFFONLY not set if [ "$OUTFILE" = '-' ]; then cat $REPTMP; ## ... cat the changes rm $REPTMP; else mv $REPTMP $FILE; ## ... do the changes fi fi test -n "$OUTFILE" && echo " --> $OUTFILE"; else echo "changes REJECTED." fi fi fi # ! diff done rm -f $REPTMP rm -f $REPDIFF #rm -f $REPBAK exit 0; ########################################