#!/bin/sh -u # # bsed - batch edit with sed # - Cameron Simpson # : ${TMPDIR:=/tmp} : ${BSEDOPTS=''} : ${DIFFOPTS='-c'} cmd=`basename "$0"` usage="Usage: $cmd [-v] [-i suf] {-f script|{-e sedcmd}...|sedcmd}... [--] files... -v Verbose. +v Not verbose. -s Silent. -d diffopt Option string for diff. -i suf Copy original to original.suf if changed. -n Passed to sed. -f script Passed to sed. -e sedcmd Passed to sed. sedcmd Passed to sed." diff=diff fflag= sedopts= sedscript= ecmd= verbose= if [ -t 1 ] then silent= else silent=1 fi ibak= set -- $BSEDOPTS ${1+"$@"} badopts= while [ $# -gt 0 ] do case $1 in --) shift ; break ;; -v) silent= verbose=1 ;; +v) verbose= ;; -s) silent=1 verbose= ;; -d) DIFFOPTS=$2; shift ;; -f) fflag=1; sedscript=$2; shift ;; -i) ibak=$2; shift ;; -i?*) ibak=`expr "x$1" : 'x-i\(.*\)'` ;; -n) sedopts="$sedopts $1" ;; -e) ecmd="$ecmd $2"; shift ;; -*) echo "$cmd: $1: unknown option" >&2 badopts=1 ;; *) break ;; esac shift done sedf="sed $sedopts" if [ $fflag ] then sedf="$sedf -f \"\$sedscript\"" else if [ -z "$ecmd" ] then case $# in 0) echo "$cmd: missing sed command" >&2 badopts=1 ;; *) ecmd=$1; shift ;; esac fi sedf="$sedf -e \"\$ecmd\"" fi [ $# = 0 ] && { echo "$cmd: missing filenames" >&2; badopts=1; } [ $badopts ] && { echo "$usage" >&2; exit 2; } tmp=$TMPDIR/$cmd.$$ trap 'rm -f "$tmp"; exit 1' 1 2 15 xit=0 ok=1 while [ $ok ] || xit=1 # catch exit from previous loop [ $# -gt 0 ] # loop condition do file=$1; shift if [ "x$file" = x- ] then [ $verbose ] && echo "reading filenames from stdin..." set x `cat` ${1+"$@"}; shift continue fi ok= if [ ! -f "$file" ] then echo "$cmd: $file: not a regular file, skipped" >&2 continue fi bakfile= bakdir= case "$ibak" in '') ;; */) case "$file" in */*) bfile=`basename "$file"` dfile=`dirname "$file"` ;; *) bfile=$file dfile=. esac bakdir=$dfile/$ibak bakfile=$bakdir/$bfile ;; *) bakfile=$file.$ibak ;; esac if [ -n "$bakfile" ] && [ -f "$bakfile" ] then echo "$cmd: $file: backup $bakfile already exists, original unchanged" >&2 continue fi [ $verbose ] && echo "$file ..." # This is a defense against files without newlines: # we append a newline and then toss the last line if it's empty. # This because sed discards newlineless trailing lines. # - Cameron Simpson 09nov2001 # if ( cat <"$file"; echo ) \ | sed -e '$!b notlast' -e '/^$/d' -e ':notlast' \ | eval "$sedf >\$tmp" then # check for changes if cmp -s "$file" "$tmp" then ok=1 # no change continue # Theoretically the sed should fail if the disc fills. # Therefore dropping this test, which is a hack and breaks things # when we really do end up with empty files. # - Cameron Simpson 09nov2001 # # else # if [ ! -s "$tmp" ] # then # echo "$cmd: warning: tmpfile empty! skipping $file" >&2 # continue # fi # fi [ $silent ] || $diff $DIFFOPTS "$file" "$tmp" # ensure we have a backup if [ -n "$bakfile" ] then if [ -f "$bakfile" ] then echo "$cmd: $file: backup $bakfile already exists, original unchanged" >&2 continue fi if cp "$file" "$bakfile" && cmp -s "$file" "$bakfile" then : else echo "$cmd: $file: can't make backup, original unchanged" >&2 continue fi fi # copy changed version in if cat "$tmp" > "$file" && cmp -s "$tmp" "$file" then ok=1 # backup & update ok continue fi echo "$cmd: $file: can't update" >&2 if [ -n "$bakfile" ] then if cat "$bakfile" > "$file" then echo "$cmd: $file: original restored" >&2 rm "$bakfile" else echo "$cmd: $file: restore failed; original left in $bakfile" >&2 fi else echo "$cmd: $file: no backup, may be corrupt" >&2 fi else echo "$cmd: $file: edit fails; original unchanged" >&2 fi done rm -f "$tmp" exit $xit