#!/bin/bash [ "$1" = "--bashx" ] && { shift ; /bin/bash -x "$0" "$@" ; exit ; } # Delete a field and shift the others to the left awk_delfield=' function delfield(n, _i) { # if(_i>=DNF) return; for(_i=n;_i columns $AWK "$awk_delfield$awk_frange$awk_ncol$awk_delete"' BEGIN{FS="/";B="'"$1"'"; E="'"$2"'"; }; { delete_a(f); n=frange(f,B,E,1); s=""; for(i=1;i<=n;i++) { printf("%s%s",s,$f[i]); s="/" } print "" }' } delpaths() { # [^][~]pat beg [end] < all_cols > remaining_cols typeset p="$1" b="$2" e="$3" if c bz ez bz="$(echo "$b"|$AWK '{print $0+0}')" ez="$(echo "$e"|$AWK '{print $0+0}')" [ "$e" = "NF" ] && { b="$3" ; e="$2" ; } [ "$bz" -lt "$ez" ] && { b="$3" ; e="$2" ; } if [ ! -z "$p" ] ; then [ "${p:0:1}" = "^" ] && { c='!'; p="${p:1}" ; } if [ "${p:0:1}" = "~" ] ; then p="${p:1}" c="$c~" else [ -z "$c" ] && c='=' c="$c=" fi if='if($f[i] '"$c"' "'"$p"'")' [ ! -z "$SW_V" ] && echo "D: $if" >&2 fi $AWK -F/ "$awk_delfield$awk_ncol$awk_frange$awk_delete"' BEGIN{ B="'"$b"'"; E="'"$e"'"; OFS="/" }; B==0 {print "";next} { delete_a(f); n=frange(f,B,E,1); for(i=1;i<=n;i++) '"$if"' delfield(f[i]) } {print_d()}' } # ----------- These shortcuts reduce buffer copying ----------- gbr() { echoNL "$out" |cutpaths $1 $2 ; } gbn() { echoNL "$out" |cutpaths $brange ; } ghn() { echoNL "$hold" |cutpaths $hrange ; } # Must not be called as a subprocess!! sbr() { out="$(echoNL "$out" |subpaths "$1" $2 $3)" ; } sbn() { out="$(echoNL "$out" |subpaths "$1" $brange)" ; } shn() { hold="$(echoNL "$hold" |subpaths "$1" $hrange)" ; } # ensure that we have at least NL lines echoNL() { echo "$1"|$AWK '1;END{for(i=NR;i<'"$NL"';i++)print ""}' ; } # ----------- ----------- extensions() { # extstring < text > replaced $AWK -F'\\.' "$awk_delfield"'BEGIN{ ext="'"$1"'"; ep=substr(ext,1,1); ext=substr(ext,2); # -|+ if(substr(ext,1,1)==ep) { ep=ep ep; ext=substr(ext,2); # --|++ } i=index(ext,"e"); es=substr(ext,1,i-1); ext=substr(ext,i+1); # n er=substr(ext,1,1); if(er=="/") ext=substr(ext,2); if(i = index(ext,"/")) { oext=substr(ext,1,i-1); ext=substr(ext,i+1); } OFS="."; esz=es+0; esf=es+1; } ep=="--" && esz { d=0; for(i=NF; i>1 && d=esf && NF>1 { if(esf>1) nf = esf ; else nf = NF if(ep=="++") { $NF = $NF "." ext # ++eExt } else if(er=="/") { if(ep=="+") { $nf= ext # +e/Ext (!forced) } else if(oext=="" || oext==$nf) $nf= ext # -[n]e/[Ext] } else if(ep=="--") { if(ext=="") { DNF=1; # --e } else { # --eExt for(i=2; i<=NF; i++) if($i==ext) delfield(i--); } # -[n]e[Ext] } else if($nf==ext || ext=="") delfield(nf) } NF==esz { if(ep=="+") $NF = $NF "." ext } # +neExt !esz && NF==1 { if(ep=="+"||ep=="++") $NF = $NF "." ext # +[+]e[/]Ext (!forced) # if(er=="/" && ep=="--") $NF = $NF "." ext # old way, use +e/Ext instead # --e/Ext (forced) } {print_d()} ' } expoptions() { # abc [legal] > -a -b -c echo | $AWK 'END{O="'"$1"'";L="'"$2"'"; if(L=="")L=O; for(i=1;i<=length(O);i++){s=substr(O,i,1); if(index(L,s)) printf("-%s ",s)}}' } subpaths() { # paths beg [end] < orig > replaced [ "$2" = "0" ] && { echoNL "$1" ; return ; } typeset awk_l="$(echoNL "$1"| $AWK "$awk_esc"'{print "L[" NR "]=\"" esc($0) "\";"}')" $AWK "$awk_delfield$awk_frange$awk_ncol$awk_delete"' BEGIN{FS="/";OFS=FS; B="'"$2"'"; E="'"$3"'";'"$awk_l"'}; { delete_a(f); n=frange(f,B,E,1); if(n) $f[1]=L[NR]; s=f[2]; for(i=2;i<=n;i++) delfield(s) } {print_d()}' } pasteCols() { # in out sep [-nb] > in out [ -z "$1$2" ] && return typeset awk_in="$(echoNL "$1" | $AWK "$awk_esc"'{print "L[" NR "]=\"" esc($0) "\";"}')" [ "$4" != "-nb" ] && { awkb='{BK=0};/^$/||L[NR]~/^$/{BK=1};' ; } echoNL "$2" | $AWK "${awkb}BEGIN{$awk_in};"'!BK{print L[NR] "'"$3"'" $0}' } # must NOT be called as a subprocess! filterN() { # [!] [-rs RS] [-fs FS] - linenumbers typeset awk_ns fs=':' rs='\n' eq='' nl while [ $# -gt 0 ] ; do case "$1" in "!") eq='!' ;; -rs) shift ; rs="$1" ;; -fs) shift ; fs="$1" ;; -) shift ; break ;; *) break ;; esac shift done [ -z "$1" ] && { in=''; out='' ; return ; } awk_ns="$(echo "$1"| $AWK 'BEGIN{FS="'"$fs"'";RS="'"$rs"'"}; $1+0 == $1 {print "L[" $1 "]=1"}')" nl="$(echo "$awk_ns" |wc -l)" hold="$(echoNL "$hold"| $AWK "BEGIN{$awk_ns}; $eq"'L[NR]')" out="$( echoNL "$out" | $AWK "BEGIN{$awk_ns}; $eq"'L[NR]')" in="$( echoNL "$in" | $AWK "BEGIN{$awk_ns}; $eq"'L[NR]')" if [ -z "$eq" ] ; then NL=$nl ; else NL=$(($NL - nl)) ; fi } diffcols() { # in out > linenumbers awk_in="$(echoNL "$1" | $AWK "$awk_esc"'{print "L[" NR "]=\"" esc($0) "\";"}')" echoNL "$2" | $AWK "BEGIN{$awk_in};"'$0 != L[NR] {print NR}' } parseRange() { # rstr > beg end [ "${1:0:1}" != "%" ] && { echo NF ; return ; } typeset b="$(echo "${1:1}"|sed -e'/^[-0-9]*/!d; s/:.*$//')" typeset s="$(echo "${1:1}"|sed -e'/^[-0-9]*:/!d; s/^.*:.*$/:/')" typeset e="$(echo "${1:1}"|sed -e'/^[-0-9]*:/!d; s/^.*://')" [ "$SW_V" = "-vr" ] && echo "bse($1):$b,$s,$e" >&2 [ "$b" = "-" ] && b=NF [ "$e" = "-" ] && e=NF if [ -z "$s" ] ; then [ -z "$b" ] && b=0 else [ -z "$b" ] && b=1 [ -z "$e" ] && e=NF fi echo $b $e } parseRanges() { # b|h|c str > b(brange),h(hrange),c(cmd) typeset b="$(echo "$2"| sed -e'/^-%/!d; s/-\(%[-0-9:]*\).*$/\1/')" typeset h="$(echo "$2"| sed -e'/^-%[-0-9:]*%/!d; s/^-%[-0-9:]*\(%[-0-9:]*\).*$/\1/')" typeset c="$(echo "$2"| sed -e's/^-[-%0-9:]*/-/')" [ "$SW_V" = "-vr" ] && echo -n "BHC($2):$b,$h,$c " >&2 case "$1" in b) parseRange "$b" ;; h) [ "${h:0:1}" != "%" ] && { parseRange "$b" ; return ; } parseRange "$h" ;; c) echo "$c" ;; esac } Usage() { cat - >&2 <&2 < "\$1"' 4) Perform a subversion mv on many c files, one entry per command (old subversion limitation): ls mypath/to/files/*/*.c | fargs -%p./newdir -%ha/ | \\ xargs -l2 svn mv Bugs: -Does not support gnu find -print0 (all user -x executed utilities would need to support it) -One tr option should be able to support 2 arguments -Reverse address range insertion is not supported -Failed operations do not cause the entire program to fail -Dot files and current directory entries may confuse extension manipulations EOF return cat - >&2 <&2 shift ; continue fi opt='' ;; *) opt='' ;; esac if [ ! -z "$opt" ] ; then [ ! -z "$SW_V" ] && echo "OPT:$1" >&2 shift ; continue fi ### Process each command ($cmd) ### [ ! -z "$SW_V" ] && echo "CMD:$cmd RANGES[main($brange),hold($hrange)]">&2 case "$cmd" in -d*) out="$(echoNL "$out"|delpaths "${cmd:2}" $brange)" ;; -p*) sbn "$(yes "${cmd:2}"|sed -e"$NL"q)" ;; [+-]e*|[+-][0-9+-]e*|[+-][+-][0-9]e*) sbr "$(gbr NF| extensions "$cmd")" NF ;; -s*) sbn "$(gbn| sed -es"${cmd:2}")" ;; -tu) sbn "$(gbn| tr '[:lower:]' '[:upper:]')" ;; -tl) sbn "$(gbn| tr '[:upper:]' '[:lower:]')" ;; -t[sd]|-t[sd][cC]|-t[cC][sd]) TOPTS="$(expoptions "${cmd:2}" "cSsd")" shift ; sbn "$(gbn| tr $TOPTS "$1")" ;; -t*) TOPTS="$(expoptions "${cmd:2}" "cSsdt")" shift ; sbn "$(gbn| tr $TOPTS "$1" "$2")" ; shift ;; -g*) GOPTS="$(expoptions "${cmd:2}" "Fwvix")" shift ; filterN - "$(gbn|$GREP -n $GOPTS "$1")" ;; -hy) shn "$(gbn)" ;; -hi) shn "$(echoNL "$in"|cutpaths $brange)" ;; -hs) tmp="$(ghn)" ; shn "$(gbn)" ; sbn "$tmp" ; unset tmp ;; -ha*) sbn "$(pasteCols "$(gbn)" "$(ghn)" "${cmd:3}")" ;; -hp*) sbn "$(pasteCols "$(ghn)" "$(gbn)" "${cmd:3}")" ;; -hc) filterN '!' - "$(diffcols "$(ghn)" "$(gbn)")" ;; -hn) filterN - "$(diffcols "$(ghn)" "$(gbn)")" ;; -x|-xf*|-xk*) unset args ; i=0 ; shift while [ $# -gt 0 -a "$1" != ";" ] ; do args[$i]="$1" ; i=$(($i+1)); shift done if [ "$cmd" = "-x" ] ; then sbn "$(gbn|"${args[@]}")" else fs="${cmd:3}" ; [ -z "$fs" ] && fs=":" f='' ; [ "${cmd:2:1}" = "f" ] && f='!' filterN $f -fs "$fs" - "$(gbn|"${args[@]}")" fi ;; *) Usage ; echo "ERR($PROG): argument not understood ($cmd)" >&2 ; exit ;; esac [ ! -z "$SW_DEB" ] && echoNL "$out">&2 shift done [ -z "$SW_COM" ] && filterN - "$(diffcols "$in" "$out")" ### --- Main Output Section --- ### [ -z "$SW_BLANK" ] && { awkb='{BK=0};/^$/{BK=1};' ; } [ ! -z "$SW_OMIT" ] && { echo "$out" |$AWK "$awkb"'BEGIN{OFS="'"$SW_OFS"'"};!BK'; exit ; } [ ! -z "$SW_BLANK" ] && { SW_BLANK='-nb' ; } if [ -z "$SW_FIRST" ] ; then pasteCols "$in" "$out" "$SW_OFS" $SW_BLANK else pasteCols "$out" "$in" "$SW_OFS" $SW_BLANK fi