diff options
Diffstat (limited to 'examples/scripts.v2')
-rw-r--r-- | examples/scripts.v2/PERMISSION | 59 | ||||
-rw-r--r-- | examples/scripts.v2/README | 33 | ||||
-rw-r--r-- | examples/scripts.v2/arc2tarz | 85 | ||||
-rw-r--r-- | examples/scripts.v2/bashrand | 76 | ||||
-rw-r--r-- | examples/scripts.v2/cal2day.bash | 49 | ||||
-rw-r--r-- | examples/scripts.v2/cdhist.bash | 176 | ||||
-rw-r--r-- | examples/scripts.v2/corename | 43 | ||||
-rw-r--r-- | examples/scripts.v2/fman | 281 | ||||
-rwxr-xr-x | examples/scripts.v2/frcp | 288 | ||||
-rw-r--r-- | examples/scripts.v2/lowercase | 44 | ||||
-rw-r--r-- | examples/scripts.v2/ncp | 187 | ||||
-rw-r--r-- | examples/scripts.v2/newext | 64 | ||||
-rw-r--r-- | examples/scripts.v2/nmv | 187 | ||||
-rw-r--r-- | examples/scripts.v2/pages | 187 | ||||
-rw-r--r-- | examples/scripts.v2/pf | 127 | ||||
-rw-r--r-- | examples/scripts.v2/pmtop | 25 | ||||
-rw-r--r-- | examples/scripts.v2/ren | 585 | ||||
-rw-r--r-- | examples/scripts.v2/rename | 122 | ||||
-rw-r--r-- | examples/scripts.v2/repeat | 121 | ||||
-rw-r--r-- | examples/scripts.v2/shprof | 66 | ||||
-rw-r--r-- | examples/scripts.v2/untar | 80 | ||||
-rw-r--r-- | examples/scripts.v2/uudec | 45 | ||||
-rw-r--r-- | examples/scripts.v2/uuenc | 69 | ||||
-rw-r--r-- | examples/scripts.v2/vtree | 137 | ||||
-rw-r--r-- | examples/scripts.v2/where | 111 |
25 files changed, 3247 insertions, 0 deletions
diff --git a/examples/scripts.v2/PERMISSION b/examples/scripts.v2/PERMISSION new file mode 100644 index 0000000..f65e848 --- /dev/null +++ b/examples/scripts.v2/PERMISSION @@ -0,0 +1,59 @@ +From spcecdt@armory.com Wed May 10 10:21:11 1995 +Flags: 10 +Return-Path: spcecdt@armory.com +Received: from po.cwru.edu (root@po.CWRU.Edu [129.22.4.2]) by odin.INS.CWRU.Edu with ESMTP (8.6.10+cwru/CWRU-2.1-ins) + id KAA22876; Wed, 10 May 1995 10:21:10 -0400 (from spcecdt@armory.com for <chet@odin.INS.CWRU.Edu>) +Received: from deepthought.armory.com (mmdf@deepthought.armory.com [192.122.209.42]) by po.cwru.edu with SMTP (8.6.10+cwru/CWRU-2.3) + id BAA16354; Wed, 10 May 1995 01:33:22 -0400 (from spcecdt@armory.com for <chet@po.cwru.edu>) +From: John DuBois <spcecdt@armory.com> +Date: Tue, 9 May 1995 22:33:12 -0700 +In-Reply-To: Chet Ramey <chet@odin.ins.cwru.edu> + "ksh scripts" (May 9, 1:36pm) +X-Www: http://www.armory.com/~spcecdt/ +X-Mailer: Mail User's Shell (7.2.5 10/14/92) +To: chet@po.cwru.edu +Subject: Re: ksh scripts +Message-ID: <9505092233.aa13001@deepthought.armory.com> + + Sure. The canonical versions are available on ftp.armory.com; you might +want to pick up the latest versions before modifying them. + + John + +On May 9, 1:36pm, Chet Ramey wrote: +} Subject: ksh scripts +} From odin.ins.cwru.edu!chet Tue May 9 10:39:51 1995 +} Received: from odin.INS.CWRU.Edu by deepthought.armory.com id aa22336; +} 9 May 95 10:39 PDT +} Received: (chet@localhost) by odin.INS.CWRU.Edu (8.6.10+cwru/CWRU-2.1-ins) +} id NAA20487; Tue, 9 May 1995 13:39:24 -0400 (from chet) +} Date: Tue, 9 May 1995 13:36:54 -0400 +} From: Chet Ramey <chet@odin.ins.cwru.edu> +} To: john@armory.com +} Subject: ksh scripts +} Cc: chet@odin.ins.cwru.edu +} Reply-To: chet@po.cwru.edu +} Message-ID: <9505091736.AA20411.SM@odin.INS.CWRU.Edu> +} Read-Receipt-To: chet@po.CWRU.Edu +} MIME-Version: 1.0 +} Content-Type: text/plain; charset=us-ascii +} Status: OR +} +} Hi. I'm the maintainer of bash (the GNU `Bourne Again shell') for +} the FSF. +} +} I picked up a tar file of ksh scripts you wrote from an anon FTP site +} a while back. I'd like your permission to include modified versions +} of some of them in the next major bash distribution (with proper credit +} given, of course). Is it OK if I do that? +} +} Chet Ramey +} +} -- +} ``The lyf so short, the craft so long to lerne.'' - Chaucer +} +} Chet Ramey, Case Western Reserve University Internet: chet@po.CWRU.Edu +}-- End of excerpt from Chet Ramey + + + diff --git a/examples/scripts.v2/README b/examples/scripts.v2/README new file mode 100644 index 0000000..b3d0559 --- /dev/null +++ b/examples/scripts.v2/README @@ -0,0 +1,33 @@ +This collection of scripts was originally written for ksh-88 by +John DuBois <spcecdt@armory.com>. The conversion to bash v2 +syntax was done by Chet Ramey. + +These scripts are as-is; there is no copyright associated with +any of them. They exist simply as examples of bash scripting. + +Here's a description of what's in this directory: + +arc2tarz Convert an "arc" archive to a compressed tar archive. +corename Tell what produced a core file. +fman Fast man replacement. +frcp Copy files using ftp but with rcp-type command line syntax. +lowercase Change filenames to lower case. +ncp A nicer front end for cp (has -i, etc.). +newext Change the extension of a group of files. +nmv A nicer front end for mv (has -i, etc.). +pages Print specified pages from files. +pf A pager front end that handles compressed files. +rename Change the names of files that match a pattern. +repeat Execute a command multiple times. +untar Unarchive a (possibly compressed) tarfile into a directory. +uudec Carefully uudecode multiple files. +uuenc uuencode multiple files. +vtree Print a visual display of a directory tree. +where Show where commands that match a pattern are. + +The following scripts were written or converted by Chet Ramey: + +bashrand Random number generator with upper and lower bounds and optional seed +cdhist cd replacement with a directory stack added +pmtop Poor man's `top' for SunOS 4.x and BSD/OS +shprof Line profiler for bash scripts diff --git a/examples/scripts.v2/arc2tarz b/examples/scripts.v2/arc2tarz new file mode 100644 index 0000000..285bede --- /dev/null +++ b/examples/scripts.v2/arc2tarz @@ -0,0 +1,85 @@ +#! /bin/bash +# +# original from: +# arc2tarz: convert arced file to tarred, compressed form. +# @(#) arc2tarz.ksh 1.0 92/02/16 +# 91/03/28 john h. dubois iii (john@armory.com) +# 92/02/16 added -h option for help +# +# conversion to bash v2 syntax by Chet Ramey + +unset ENV +Usage="Usage: $0 arcfile [-hcg] [ tarzfile ]" + +phelp() +{ +echo "$Usage +arcfile is the name of an arc file to convert to tarred, compressed form. +The file must have a .arc extension, but only the base name needs to be +given. If no output file name is given, it will be created in the current +directory with the name being the arcfile basename followed by .tar.EXT. +If the -c option is given, compress will be used, and EXT will be Z. +The default (also available with -g) is to use gzip, in which case EXT +is gz. If the basename is too long the extension may be truncated. All +uppercase letters in the names of files in the archive are moved to lowercase." +} + +compress=gzip +ext=gz + +while getopts "hcg" opt; do + case "$opt" in + h) phelp; exit 0;; + c) compress=compress; ext=Z;; + g) compress=gzip ; ext=gz ;; + *) echo "$Usage" 1>&2 ; exit 2;; + esac +done + +shift $((OPTIND - 1)) + +if [ $# = 0 ]; then + phelp + exit 0 +fi + +[ -z "$TMP" ] && tmpdir=/tmp/arc2tarz.$$ || tmpdir=$TMP/arc2tarz.$$ + +case "$1" in +*.arc) arcfile=$1 ;; +*) arcfile=$1.arc ;; +esac + +if [ ! -f $arcfile ] || [ ! -r $arcfile ]; then + echo "Could not open arc file \"$arcfile\"." + exit 1 +fi + +case "$arcfile" in +/*) ;; +*) arcfile=$PWD/$arcfile ;; +esac + +basename=${arcfile%.arc} +basename=${basename##*/} +[ $# -lt 2 ] && tarzname=$PWD/$basename.tar.$ext || tarzname=$2 + +trap 'rm -rf $tmpdir $tarzname' 1 2 3 6 15 + +mkdir $tmpdir +cd $tmpdir +echo "unarcing files..." +arc -ie $arcfile + +# lowercase +for f in *; do + new=$(echo $f | tr A-Z a-z) + if [ "$f" != "$new" ]; then + mv $f $new + fi +done + +echo "tarring/compressing files..." +tar cf - * | $compress > $tarzname +cd - +rm -rf $tmpdir diff --git a/examples/scripts.v2/bashrand b/examples/scripts.v2/bashrand new file mode 100644 index 0000000..54260c0 --- /dev/null +++ b/examples/scripts.v2/bashrand @@ -0,0 +1,76 @@ +#! /bin/bash +# bashrand - generate a random number in a specified range with an +# optionally specified ``seed'' value. +# +# Original Author: Peter Turnbull, May 1993 + +usage() +{ + echo "$PROG: usage: $PROG [-s seed] lower-limit upper-limit" >&2 +} + +PROG=${0##*/} + +SEED=$$ # Initialize random-number seed value with PID + +while getopts s: opt +do + case "$opt" in + s) SEED=$OPTARG ;; + *) usage ; exit 2 ;; + esac +done + +shift $((OPTIND - 1)) + +# Process command-line arguments: +case $# in + 2) Lower=$1; Upper=$2 ;; + *) usage ; exit 2 ;; +esac + +# Check that specified values are integers: +expr "$Lower" + 0 >/dev/null 2>&1 || { + echo "$PROG: lower ($Lower) not an integer" >&2 + exit 1 +} + +expr "$Upper" + 0 >/dev/null 2>&1 || { + echo "$PROG: upper ($Upper) not an integer" >&2 + exit 1 +} + +expr "$SEED" + 0 >/dev/null 2>&1 || { + echo "$PROG: seed ($SEED) not an integer" >&2 + exit 1 +} + +# Check that values are in the correct range: +(( $Lower < 0 )) || [ `expr "$Lower" : '.*'` -gt 5 ] && { + echo "$PROG: Lower limit ($Lower) out of range" >&2 + exit 1 +} + +(( $Upper > 32767 )) || [ `expr "$Upper" : '.*'` -gt 5 ] && { + echo "$PROG: Upper limit ($Upper) out of range" >&2; + exit 1 +} + +(( $SEED < 0 )) || (( $SEED > 32767 )) || [ `expr "$SEED" : '.*'` -gt 5 ] && { + echo "$PROG: Seed value ($SEED) out of range (0 to 32767)" >&2 + exit 1 +} + +(( $Upper <= $Lower )) && { + echo "$PROG: upper ($Upper) <= lower value ($Lower)" >&2 + exit 1 +} + +# Seed the random-number generator: +RANDOM=$SEED + +# Compute value, scaled within range: +let rand="$RANDOM % ($Upper - $Lower + 1) + $Lower" + +# Report result: +echo $rand diff --git a/examples/scripts.v2/cal2day.bash b/examples/scripts.v2/cal2day.bash new file mode 100644 index 0000000..f26128b --- /dev/null +++ b/examples/scripts.v2/cal2day.bash @@ -0,0 +1,49 @@ +#!/bin/bash +# cal2day - "parse" appropriate calendar output to match date number +# with day name. +# +# usage: cal2day month day [year] +# +# ORIGINAL *TAG:33239 3:Dec 9 1997:0755:sh.d/cal2day: +# +# Obtained from usenet +# +# Converted to bash v2 syntax by Chet Ramey <chet@po.cwru.edu> + +#1 PARSE OPTIONS +while getopts :dls _inst +do case $_inst in + (d) format='%1d%.0s\n' ;; # 0, 1, ..., 7 + (l) format='%0.s%-s\n' ;; # Sunday, Monday, ..., Saturday + (s) format='%0.s%-.3s\n' ;; # Sun, Mon, ..., Sat + esac +done +shift $((OPTIND-1)) + +#2 PARAMETER VALUES +((!$#)) && set -- $(date '+%m %d') +: ${format:='%0.s%-.3s\n'} +: ${1:?missing month parameter [1-12]} +: ${2:?missing day parameter [1-31]} + +#3 CALCULATE DAY-OF-WEEK FROM DATE +cal $1 ${3:-$(date +%Y)} | gawk -FX ' +BEGIN { day="Sunday Monday Tuesday WednesdayThursday Friday Saturday" + sub(/^0/, "", daynum) + dayre="(^| )" daynum "( |$)" + } +#NR==2 { print length($0) } +NR==1 || NR==2 \ + { next } +dayre { if (match($0, dayre)) + { #print RSTART, RLENGTH, substr($0, RSTART, RLENGTH) + if (daynum<=9 || RSTART==1) RSTART-=1 + exit + } + } +END { # 20/21 char width assumed + printf format, RSTART/3, substr(day, RSTART*3+1, 9) + } +' daynum=$2 format=$format - + +exit 0 diff --git a/examples/scripts.v2/cdhist.bash b/examples/scripts.v2/cdhist.bash new file mode 100644 index 0000000..df8aea7 --- /dev/null +++ b/examples/scripts.v2/cdhist.bash @@ -0,0 +1,176 @@ +#! /bin/bash +# +# cdhist - cd replacement with a directory stack like pushd/popd +# +# usage: cd [-l] [-n] [-] [dir] +# +# options: +# -l print the cd directory stack, one entry per line +# - equivalent to $OLDPWD +# -n cd to nth directory in cd directory stack +# -s cd to first directory in stack matching (substring) `s' +# +# arguments: +# dir cd to dir and push dir onto the cd directory stack +# +# If the new directory is a directory in the stack and the options selected +# it (-n, -s), the new working directory is printed +# +# If the variable CDHISTFILE is set, the cd directory stack is loaded from +# and written to $CDHISTFILE every time `cd' is executed. +# +# Note: I got this off the net somewhere; I don't know the original author +# +# Chet Ramey +# chet@po.cwru.edu + +_cd_print() +{ + echo -e "$@" +} + +cd() +{ + typeset -i cdlen i + typeset t + + if [ $# -eq 0 ] + then + set -- $HOME + fi + + if [ "$CDHISTFILE" ] && [ -r "$CDHISTFILE" ] # if directory history exists + then + typeset CDHIST + i=-1 + while read -r t # read directory history file + do + CDHIST[i=i+1]=$t + done <$CDHISTFILE + fi + + if [ "${CDHIST[0]}" != "$PWD" ] && [ -n "$PWD" ] + then + _cdins # insert $PWD into cd history + fi + + cdlen=${#CDHIST[*]} # number of elements in history + + case "$@" in + -) # cd to new dir + if [ "$OLDPWD" = "" ] && ((cdlen>1)) + then + '_cdprint' ${CDHIST[1]} + builtin cd ${CDHIST[1]} + pwd + else + builtin cd "$@" + # pwd + fi + ;; + -l) # _cdprint directory list + ((i=cdlen)) + while (((i=i-1)>=0)) + do + num=$i + '_cdprint' "$num ${CDHIST[i]}" + done + return + ;; + -[0-9]|-[0-9][0-9]) # cd to dir in list + if (((i=${1#-})<cdlen)) + then + '_cdprint' ${CDHIST[i]} + builtin cd ${CDHIST[i]} + pwd + else + builtin cd $@ + # pwd + fi + ;; + -*) # cd to matched dir in list + t=${1#-} + i=1 + while ((i<cdlen)) + do + case ${CDHIST[i]} in + *$t*) + '_cdprint' ${CDHIST[i]} + builtin cd ${CDHIST[i]} + pwd + break + ;; + esac + ((i=i+1)) + done + if ((i>=cdlen)) + then + builtin cd $@ + # pwd + fi + ;; + *) # cd to new dir + builtin cd $@ + # pwd + ;; + esac + + _cdins # insert $PWD into cd history + + if [ "$CDHISTFILE" ] + then + cdlen=${#CDHIST[*]} # number of elements in history + + i=0 + while ((i<cdlen)) + do + echo ${CDHIST[i]} # update directory history + ((i=i+1)) + done >$CDHISTFILE + fi +} + +_cdins() # insert $PWD into cd history +{ # meant to be called only by cd + typeset -i i + + i=0 + + while (( i < ${#CDHIST[*]} )) # see if dir is already in list + do + if [ "${CDHIST[$i]}" = "$PWD" ] + then + break + fi + ((i=i+1)) + done + + if (( i>22 )) # limit max size of list + then + i=22 + fi + + while (((i=i-1)>=0)) # bump old dirs in list + do + CDHIST[i+1]=${CDHIST[i]} + done + + CDHIST[0]=$PWD # insert new directory in list +} + +# examples +shopt -s expand_aliases + +# go to known place before doing anything +cd / + +echo CDHIST: "${CDHIST[@]}" +for dir in /tmp /bin - -2 -l +do + cd $dir + echo CDHIST: "${CDHIST[@]}" + echo PWD: $PWD + +done + +exit 0 diff --git a/examples/scripts.v2/corename b/examples/scripts.v2/corename new file mode 100644 index 0000000..2b51e5d --- /dev/null +++ b/examples/scripts.v2/corename @@ -0,0 +1,43 @@ +#! /bin/bash +# +# original from: +# @(#) corename.ksh 1.0 93/04/01 +# 92/11/11 john h. dubois iii (john@armory.com) +# 92/02/16 Added help option. +# 92/02/22 Added cd to origdir to fix prob w/multiple relative paths. +# 93/04/01 Added check for whether file exists. +# +# conversion to bash v2 syntax done by Chet Ramey + +# inspired by belal's equivalent utility + +if [ "$1" = -h ]; then + echo \ +"$0: print the names of executables that dumped core. +Usage: $0 [corename ...] +If no corename is given, \"core\" is assumed." + exit 0 +fi + +[ $# = 0 ] && set core +origdir=$PWD +for i; do + cd $origdir + file=${i##*/} + dir=${i%$file} + [ -z "$dir" ] && dir=$origdir/ + if [ ! -f $dir$file ]; then + echo "$dir$file: No such file." + continue + fi + if [ ! -r $dir$file ]; then + echo "$dir$file: Cannot open." + continue + fi + cd $dir + + # the adb output syntax is highly variable. this works on SunOS 4.x + set -- $(adb $file < /dev/null 2>&1 | sed 1q) + name=${7#??} + echo "$i: ${name%??}" +done diff --git a/examples/scripts.v2/fman b/examples/scripts.v2/fman new file mode 100644 index 0000000..1e94d21 --- /dev/null +++ b/examples/scripts.v2/fman @@ -0,0 +1,281 @@ +#! /bin/bash +# +# original from: +# fman: new man program +# @(#) fman.ksh 1.5 94/04/16 +# 91/07/03 john h. dubois iii (john@armory.com) +# 91/07/11 made it unpack man pages if neccessary +# 91/07/16 fixed test for whether man file pattern was expanded +# 92/01/21 made it read /etc/default/man to get section order, +# and only display the first section found. +# 92/02/06 changed name to fman +# 92/02/07 fixed bug in notfound +# 92/02/13 incorporated changes from DOS version +# 92/03/11 changed to use MANPATH from environment if set, +# and search all directories given in MANPATH +# 92/03/15 exec pager or man w/o forking +# 92/05/31 try using index if one exists +# 92/10/01 Added "See also <other sections>" +# 92/10/18 If PAGER is less, search for name of man page to make it easier +# to find information in man pages for multiple items +# 92/11/11 Make it work for compressed files not listed in index; +# deal with man pages listed in index that don't exist. +# 93/03/30 Fixed bug in MANPATH processing +# 93/06/17 Include paths in "See also:" message if they would be needed +# to get to a man page. Allow MANPATH spec on command line. +# 93/07/09 Added -h and -e options. +# 94/04/16 Added x option. +# +# conversion to bash v2 syntax done by Chet Ramey + +istrue() +{ + test 0 -ne "$1" +} + +isfalse() +{ + test 0 -eq "$1" +} + +# Finds all sections that man page $1 is in and puts them in the the +# global array Sections[]. +# The filename of each page is put in FileNames[] with the same index. +# Global vars used: +# patharr[] MANPATH directories. + +FindSectionsInIndex () +{ + typeset index indexes section mpath page=$1 + typeset -i i=0 NIndex=0 + + for mpath in "${patharr[@]}"; do + if [ -r $mpath/index ]; then + indexes="$indexes $mpath/index" + let NIndex+=1 + fi + done + [ -z "$indexes" ] && return + # Make grep give filename + [ NIndex -lt 2 ] && indexes="$indexes /dev/null" + # set positional parameters to + # indexfile:searchname pagename section ... + # e.g. + # /usr/man/index:FP_OFF Routines DOS + set -- `grep "^$page[ ]" $indexes` + while [ $# -gt 2 ]; do + FileNames[i]=${1%%index*}cat$3/$2.$3 + Sections[i]=$3 + shift 3 + let i+=1 + done +} + +# Finds all sections that man page $1 is in by searching each man directory +# in the order given in patharr[], +# and puts them in the the global array Sections[]. +# The filename of each page is put in FileNames[] with the same index. +# Global vars used: +# patharr[] MANPATH directories. +FindSectionsInDirs () +{ + local page=$1 mpath AllPaths Path + typeset -i i + + for mpath in "${patharr[@]}"; do + AllPaths="$AllPaths $mpath/cat[0-9]*/$page.* $mpath/man[0-9]*/$page.*" + done + + i=0 + for Path in $AllPaths; do + istrue $debug && echo Path = $Path + case "$Path" in + *\*) ;; + *) + # Remove compressed-file suffix to make FileNames be the same + # as it is when built by FindSectionsInIndex() + FileNames[i]=${Path%.[zZ]} + Path=${Path%/*} + Sections[i]=${Path##*/*.} + let i+=1 ;; + esac + done +} + +# FindSection: display man page. +# Uses ordarr[] (built from $ORDER) to display the version of the man +# page that occurs first in $ORDER. +# Sections[] gives the sections that a man page was found in. +# If the global variable "exist" is set to 1, nothing is displayed; +# the function instead returns zero if a page is found, nonzero if not. +# The filename of each page is in FileNames[] with the same index. +# Global vars used: +# Sections[], FileNames[], ordarr[] +FindSection () +{ + typeset -i NumPages i foundsec + local section OtherSec filename NPAGER=$PAGER POpt page=$1 Pat + local PageFile + + NumPages=${#Sections[*]} # Number of versions of man page found. + isfalse $NumPages && return 1 + case "$PAGER" in + *less) Popt="-p$page" ;; + esac + + # For each section in ORDER, determine if any man page was found in + # that section + for section in "${ordarr[@]}"; do + i=0 + foundsec=0 + while [ $i -lt $NumPages ]; do + if [ "${Sections[i]}" = $section ]; then + # Found a man page from this section of ORDER + filename=${FileNames[i]} + if [ -z "$PageFile" ]; then + PageFile=$filename + else + if istrue $foundsec; then + OtherSec="$OtherSec$page(${filename%/*/*} $section) " + else + OtherSec="$OtherSec$page($section) " + fi + fi + foundsec=1 + istrue $exist && return + fi + let i+=1 + done + done + # No pages with the specified section found. + [ -z "$PageFile" ] && return 1 + # Return if all we want to know is whether the man page exists. + [ "$exist" = 1 ] && return 0 + if [ -z "$OtherSec" ]; then + NPAGER="exec $PAGER" + fi + if [ -r $PageFile ]; then + $NPAGER $POpt $PageFile + elif [ -r $PageFile.z ]; then + pcat $PageFile.z | $NPAGER $POpt + elif [ -r $PageFile.Z ]; then + zcat $PageFile.Z | $NPAGER $POpt + elif [ -f $PageFile.gz ]; then + gzip -dc $PageFile.gz | $NPAGER $POpt + else + echo "$PageFile: cannot open." 1>&2 + OtherSec= + unset Sections[i] + let i+=1 + continue + fi + echo "See also $OtherSec" + exit 0 +} + +phelp() +{ +echo "$name: print man pages. +$name locates and prints the specified manual pages from the online UNIX +documentation. +$Usage +Options: +-e: Determine whether the specified man page exists. Nothing is printed; + $0 exits with a zero status if the page exists and a nonzero status if + it does not. +-h: Print this help." +} + +# main program + +typeset -i exist=0 debug=0 + +name=${0##*/} +Usage="Usage: $name [-eh] [[manpath] section] command-name" + +while getopts :hex opt; do + case $opt in + h) phelp; exit 0;; + e) exist=1 ;; + x) debug=1 ;; + +?) echo "$name: options should not be preceded by a '+'." 1>&2; exit 2;; + ?) + echo "$name: $OPTARG: bad option. Use -h for help." 1>&2 ; exit 2 ;; + esac +done + +# remove args that were options +shift $((OPTIND-1)) + +if [ $# -lt 1 ]; then + echo -e "$Usage\nUse -h for help." 1>&2 + exit +fi + +P=$PAGER +O=1:n:l:6:8:2:3:4:5:7:p:o +T=$TERM +M=${MANPATH:-/usr/local/man:/usr/man} +[ -f /etc/default/man ] && . /etc/default/man +[ -n "$P" ] && PAGER=$P +[ -n "$O" ] && ORDER=$O +[ -n "$T" ] && TERM=$T +[ -n "$M" ] && MANPATH=$M + +case $# in +0) echo "No man page specified." ; exit 1;; +1) page=$1;; +2) ORDER=$(echo $1 | tr a-z A-Z) ; page=$2;; +3) MANPATH=$1 + [ -n "$2" ] && ORDER=$(echo $2 | tr a-z A-Z) + page=$3;; +*) echo "Too many arguments."; exit 1;; +esac + +aargs=("$@") +[ ! -t 0 ] && PAGER=cat + +OIFS=$IFS +IFS=: +patharr=($MANPATH) +i=0 +for d in $MANPATH; do + for sec in $ORDER; do + ordarr[i]=$d/cat${sec} + let i+=1 + ordarr[i]=$d/man${sec} + let i+=1 + done +done +IFS=$OIFS + +istrue $debug && echo patharr = "${patharr[@]}" + +# if less or more is being used, remove multiple blank lines +export LESS="-s $LESS" +export MORE="-s $MORE" + +# Try using index +FindSectionsInIndex "$page" +# Exit 0 if a page was found and we're just testing for existence. +FindSection "$page" && exit 0 + +# Try searching directories +unset Sections[*] +FindSectionsInDirs "$page" +FindSection "$page" && exit 0 + +istrue $exist && exit 1 + +# Try using man +# If using more or less, make man run faster by letting more or less compress +# multiple blank lines instead of rmb +#case "$PAGER" in +#*more|*less) manopt=-b;; +#esac + +#cmd=(man $manopt -p$PAGER "${aargs[@]}") +export PAGER +cmd=(man $manopt "${aargs[@]}") +istrue $debug && echo "$name: running ${cmd[*]}" 1>&2 +exec "${cmd[@]}" diff --git a/examples/scripts.v2/frcp b/examples/scripts.v2/frcp new file mode 100755 index 0000000..572aa7b --- /dev/null +++ b/examples/scripts.v2/frcp @@ -0,0 +1,288 @@ +#! /bin/bash +# +# original from: +# +# @(#) frcp.ksh 2.2 93/11/14 +# 92/06/29 john h. dubois iii (john@armory.com) +# 92/10/14 Cleaned up, improved, added -d and -r options +# 92/11/11 Made work with a dest of '.' +# 93/07/09 Added -l and -n options, & login as anonymous if no .netrc entry +# 93/11/14 Use either passwd or password in .netrc, since ftp does. +# +# conversion to bash v2 syntax by Chet Ramey +# +# frcp: ftp front end with rcp-like syntax. +# Note: requires any machine names given to be listed with +# user and password in .netrc. If not, anonymous FTP is +# done. +# +# full path to ftp binary +if [ -x /usr/bin/ftp ]; then + FTP=/usr/bin/ftp; +elif [ -x /usr/ucb/ftp ]; then + FTP=/usr/ucb/ftp +else + FTP=ftp +fi + +istrue() +{ + test 0 -ne "$1" +} +isfalse() +{ + test 0 -eq "$1" +} + +# For each filename given, put the filename in filename[n] +# and the machine it is on in machine[n]. +function SplitNames { + typeset file + typeset -i i=1 + + unset filename[*] machine[*] + for file; do + case "$file" in + *:*) machine[i]=${file%%:*} ;; + *) machine[i]=$LocalMach ;; + esac + filename[i]=${file#*:} + let i+=1 + done +} + +function verboseprint { + echo "$@" + echo "$@" 1>&2 +} + +function MakeDir { + OFS=$IFS + local IFS=/ dir component + + case "$1" in + /*) ;; + *) dir=. + esac + set -- $1 + IFS=$OFS + for component; do + dir=$dir/$component + if [ ! -d "$dir" ]; then + if mkdir "$dir"; then :; else + echo "Could not make directory $dir." >&2 + return 1 + fi + fi + done + return 0 +} + +lastisdot () +{ + case "$1" in + */.|*/..) return 0;; + *) return 1;; + esac +} + +# CopyFiles: issue ftp(TC) commands to copy files. +# Usage: CopyFiles [sourcemachine:]sourcepath ... [destmachine:]destpath +# Global vars: +# Uses LocalMach (should be name of local machine) +# Sets global arrs machine[]/filename[] +function CopyFiles { + unset machine[*] filename[*] + + SplitNames "$@" # split names into filename[1..n] and machine[1..n] + + local DestMach=${machine[$#]} # Machine to copy files to + local DestPath=${filename[$#]} # Destination file/dir + + unset machine[$#] filename[$#] + + [ -z "$DestPath" ] && DestPath=. # dest was given as machine: + + # Try to determine if destination should be a directory + # so that it can be forced to be a directory. + + case "$DestPath" in + */) ;; # don't add / if trailing / already present + *) if [ $# -gt 2 ] || # if more than two args given, last must be a dir + # If dest in on local machine, check whether it is a directory + [ $DestMach = $LocalMach ] && [ -d "$DestPath" ] || + # If dest ends with . or .., it is a directory + lastisdot "$DestPath" + then + DestPath=$DestPath/ + fi ;; + esac + + # If one of the above tests made us think dest is a directory, + # but it isn't, complain + case "$DestPath" in + */) if [ "$DestMach" = "$LocalMach" ] && [ ! -d "$DestPath" ]; then + echo "Destination is not a directory." 1>&2 + exit 1 + fi ;; + esac + + DoCopy "$DestMach" "$DestPath" +} + +# Usage: OpenMachine machine-name +# Emits login sequence or doesn't, depending on .netrc file and global +# variables anon and noanon +OpenMachine () +{ + local machine=$1 netrc=$HOME/.netrc user= password= + + if isfalse $anon && [ -r $netrc ]; then + set -- $(gawk ' + /machine (.* )?'"$machine"'($| )/,/^ *$/ { + Fields[$1] = $2 + if ("passwd" in Fields) + Fields["password"] = Fields["passwd"] + if ("login" in Fields && "password" in Fields) { + print Fields["login"] " " Fields["password"] + exit + } + } + ' $netrc ) + user=$1 + password=$2 + fi + if [ -z "$password" ]; then + if istrue $noanon; then + echo "No .netrc entry for machine $machine" 1>&2 + exit 1 + fi + user=anonymous + password=$USER@$LocalMach + fi + verboseprint open $machine + echo user $user "*******" 1>&2 + echo user $user $password +} + +# Usage: DoCopy destination-machine destination-path +# Copies the files in global arrs machine[]/filename[] to the given dest +# Global vars: +# Uses machine[], filename[], LocalMach, check +DoCopy () +{ + local DestMach=$1 + local DestPath=$2 + local OpenMach # Machine that connection is currently open to + local OWD=$PWD SourceMach SourceFile + local FileName + typeset -i i=1 + + while [ $i -le ${#machine[*]} ]; do + istrue $check && verboseprint "runique" + + SourceMach=${machine[i]} + SourceFile=${filename[i]} + + DestFile=$DestPath + # if DestPath is a dir, + # add source filename to it without source path + case "$DestFile" in + */) DestFile=$DestFile${SourceFile##*/} ;; + esac + + if [ $SourceMach = $LocalMach ]; then + if [ $DestMach != "$OpenMach" ]; then + OpenMachine $DestMach + OpenMach=$DestMach + fi + verboseprint put $SourceFile $DestFile + elif [ $DestMach = $LocalMach ]; then + if istrue $check && [ -f "$DestFile" ]; then + echo "$DestFile already exists." 1>&2 + continue + fi + # If destination is on local machine, + # the dest will be a full dir/filename + if istrue $createdirs; then + MakeDir "${DestFile%/*}" || continue + fi + if [ $SourceMach != "$OpenMach" ]; then + OpenMachine $SourceMach + OpenMach=$SourceMach + fi + # If source filename has wildcards ([, ], *, ?) do an mget + case "$SourceFile" in + \[*\]|*\**|*\?*) + verboseprint lcd "$DestFile" + verboseprint mget "$SourceFile" + verboseprint lcd $OWD ;; + *) verboseprint get "$SourceFile" "$DestFile" ;; + esac + else + echo "Neither source machine \"$SourceMach\" "\ +"nor destination machine \"$DestMach\" is local." 1>&2 + fi + let i+=1 + done +} + +# Start of main program +name=${0##*/} + +if [ "$1" = -h ]; then + echo \ +"$name: do ftp transfers using rcp-style parameters. +Usage: $name <source> <destpath> or $name <source> [<source> ...] <destdir> +At least one of <source> and <destpath> must be the local system. +A remote filename is given as machinename:filename +If remote filenames contain wildcards, they will be globbed on the remote +machine. Make sure they are quoted when $name is invoked. +If the invoking user's .netrc file (see ftp(TC)) contains an entry for the +remote system with a login and password supplied, $name will log in using +the given login and password. If not, $name will login in as user +anonymous and with the user@localsystem as the password. +Options: +-c: check: do not overwrite files. +-d: create directories as needed. +-f: force: overwrite files (default). +-h: print this help. +-l: fail if there is no entry with login and password for the remote system, + instead of logging in as anonymous. +-n: log in as anonymous even if there is an entry for the remote system in + the user's .netrc file. +-r: read source/dest filename pairs from the standard input, + one pair per line, and copy files accordingly." + exit 0 +fi + +typeset -i check=0 createdirs=0 readinput=0 anon=0 noanon=0 + +while getopts :cdflnr Option +do + case "$Option" in + c) check=1;; + d) createdirs=1;; + f) check=0;; + l) noanon=1;; + n) anon=1;; + r) readinput=1;; + \?) echo "$OPTARG: invalid option."; exit 1;; + esac +done + +shift $((OPTIND-1)) + +LocalMach=`hostname` + +if istrue $readinput; then + while read line; do + CopyFiles $line + done | $FTP -nv +else + if [ $# -lt 2 ]; then + echo "$name: Not enough arguments. Use -h for help." 1>&2 + exit + fi + CopyFiles "$@" | $FTP -nv +fi diff --git a/examples/scripts.v2/lowercase b/examples/scripts.v2/lowercase new file mode 100644 index 0000000..fd2ec5d --- /dev/null +++ b/examples/scripts.v2/lowercase @@ -0,0 +1,44 @@ +#! /bin/bash +# +# original from +# @(#) lowercase.ksh 1.0 92/10/08 +# 92/10/08 john h. dubois iii (john@armory.com) +# +# conversion to bash v2 syntax done by Chet Ramey + +Usage="Usage: $name file ..." +phelp() +{ +echo "$name: change filenames to lower case. +$Usage +Each file is moved to a name with the same directory component, if any, +and with a filename component that is the same as the original but with +any upper case letters changed to lower case." +} + +name=${0##*/} + +while getopts "h" opt; do + case "$opt" in + h) phelp; exit 0;; + *) echo "$Usage" 1>&2; exit 2;; + esac +done + +shift $((OPTIND - 1)) + +for file; do + filename=${file##*/} + case "$file" in + */*) dirname=${file%/*} ;; + *) dirname=. ;; + esac + nf=$(echo $filename | tr A-Z a-z) + newname="${dirname}/${nf}" + if [ "$nf" != "$filename" ]; then + mv "$file" "$newname" + echo "$0: $file -> $newname" + else + echo "$0: $file not changed." + fi +done diff --git a/examples/scripts.v2/ncp b/examples/scripts.v2/ncp new file mode 100644 index 0000000..c91ba64 --- /dev/null +++ b/examples/scripts.v2/ncp @@ -0,0 +1,187 @@ +#! /bin/bash +# +# original from: +# @(#) ncp.ksh,nmv.ksh 1.1 94/07/23 +# 92/01/18 john h. dubois iii (john@armory.com) +# 92/01/31 added check for no args left after shifts +# 92/02/17 added help +# 92/02/25 remove path component from filename before tacking it onto dest. +# 92/03/15 exec mv or cp +# 93/07/13 Added -i +# 93/09/29 Made abort if file exists optional. +# 93/11/19 Exit before invoking mv if no files to move +# 94/01/03 Added o option +# 94/04/13 Added x option. +# Fixed appending of source filename, broken by earlier change. +# 94/07/23 Append only the filename part of the source path. +# +# conversion to bash v2 syntax done by Chet Ramey + +false() +{ + return 1 +} + +true() +{ + return 0 +} + +phelp() +{ +echo "$name: do a $cmd with extra checking and options. +$Usage +$name is used as a front end for $cmd to get the [icfo] options, and so +that a trailing / will force the last component of the path to be +interpreted as a directory, so that $name foo bar/ will fail if bar is +not an existing directory, instead of changing the name of foo to bar. +Effectively, $name foo bar/ is short for $name foo bar/foo +Options: +-h prints this help. +-c checks first for the existence of each file, and fails if it exists. +-i is like -c except that if the file exists and stdin and stdout are a + tty, a query is printed and a reply is read; a file is overwritten only + if the reply begins with 'y'. +-f unsets -c and -i (in case $cmd is aliased to $name). +-o (overwrite only) checks that the named file(s) exist and fails for any + that do not. It is the complement of the -c option. +Whichever of [cifo] comes later on the command line determines the behaviour. +Any of these options must come before any standard $cmd options." +} + +# interactive: Attempt to overwrite file should result in interactive +# query rather than automatic failure. +# noover: Do not overwrite files (if interactive is true, query, else fail) +# overwrite: Only overwriting is allowed, not creation of new files. +# debug: Print debugging info. +typeset interactive=false noover=false overwrite=false debug=false +name=${0##*/} + +case "$name" in +ncp|nmv) cmd=/bin/${name#?} ;; +*) echo "$name: Must be invoked as ncp or nmv." 1>&2 ; exit 2;; +esac + +Usage="Usage: $name [-cfhio] $cmd-cmd-line" + +while getopts :cfhiox opt; do + case $opt in + h) phelp; exit 0;; + x) debug=true ;; + c) noover=true ;; + i) noover=true ; interactive=true ;; + f) noover=false ; interactive=false ;; + o) overwrite=true ; noover=false ; interactive=false;; + +?) echo "$name: options should not be preceded by a '+'." 1>&2; exit 2;; + ?) echo "$name: $OPTARG: bad option. Use -h for help." 1>&2 ; exit 2;; + esac +done + +# remove args that were options +shift $((OPTIND - 1)) + +if [ $# -lt 2 ]; then + echo -e "$Usage\nUse -h for help." + exit +fi + +Check() +{ + if [ ! -f "$1" ] && $overwrite; then + echo "$name: $1: File does not exist." 1>&2 + return 1 + elif [ -f "$1" ] && $noover; then + if [ $interactive = false ] || [ ! -t 0 ] || [ ! -t 1 ]; then + echo "$name: $1: File exists." 1>&2 + return 1 + else + while :; do + echo -n \ +"$name: $1: File exists. Overwrite? (y)es/(n)o/(a)bort/(Y)es for all: " 1>&2 + read reply + case "$reply" in + y*) + echo "$name: Overwriting $1." + return 0 + ;; + Y*) + echo "$name: Overwriting $1." + interactive=false + noover=false + return 0 + ;; + [nN]*) + echo "$name: Skipping $2." + return 1 + ;; + [aA]*) + echo "$name: Aborting." + exit 1 + ;; + *) + echo "$name: Invalid response." 1>&2 + ;; + esac + done + fi + else + return 0 + fi +} + +# i is the index of the filename being examined +# lastarg is the index of the last filename before the dest directory name +typeset -i i=0 lastarg=$(($#-1)) + +# Sets argv[0..$#-1] +argv=("$@") +$debug && echo argv = "${argv[@]}" 1>&2 +dest=${argv[lastarg]} + +if $debug; then + echo \ +"interactive=$interactive noover=$noover overwrite=$overwrite debug=$debug +lastarg=$lastarg dest=$dest name=$name cmd=$cmd +files=$*" 1>&2 +fi + +if $noover || $overwrite; then + $debug && echo "checking for existance of directories..." 1>&2 + # If the destination is not intended to be a directory... + if [ $# -eq 2 ] && [ ! -d "$dest" ]; then + Check "$dest" "$1" || exit 0 # No files to copy + else + while [ $i -lt $lastarg ]; do + Check "$dest/${argv[i]##*/}" "${argv[i]}" || unset argv[i] + let i+=1 + done + fi +fi + +[ ${#argv[@]} -lt 2 ] && exit 0 + +# If only 2 args are given, mv/cp will not insist that the destination +# be a directory, which we want if the destination ends in "/" or if +# the original number of args was >2. +# $# is still the original number of args. +# Tack the file name onto the destination to force this behaviour. + +lastisslash() +{ + case "$1" in + */) return 0;; + *) return 1;; + esac +} + +if [ ${#argv[@]} = 2 ] && { lastisslash "$2" || [ $# -gt 2 ]; }; then + $debug && echo "Appending filename." 1>&2 + # Don't know which element of argv[] holds the source filename, + # since may have started with more than 1 source file & had some unset. + # So, compact args to make it easy to find the set one. + argv=("${argv[@]}") + argv[1]="${argv[1]}/${argv[0]##*/}" +fi + +$debug && echo "Executing command: $cmd ${argv[@]}" 1>&2 +exec $cmd "${argv[@]}" diff --git a/examples/scripts.v2/newext b/examples/scripts.v2/newext new file mode 100644 index 0000000..37645bd --- /dev/null +++ b/examples/scripts.v2/newext @@ -0,0 +1,64 @@ +#! /bin/bash +# +# original from: +# newext: change filename extension +# @(#) newext.sh 1.1 93/04/13 +# 90/06/06 john h. dubois iii (john@armory.com) +# 90/11/14 changed ksh-specific code to hybrid: if running under Bourne, +# uses expr instead of ksh builtin ops. Removed SYSV specific code. +# 91/08/06 added -t option +# 92/11/06 made earlier code actually work! +# 93/04/13 If no filenames given, act on files in current dir +# +# conversion to bash v2 syntax by Chet Ramey + +usage="Usage: newext [-th] <oldext> <newext> [filename ...]" + +phelp() +{ +echo "$usage +Rename all given files that end in oldext with newext replacing oldext. +If no filenames are given, all files in the current directory that end +in oldext are acted on (no filename is equivalent to '*'). +Options: +-h: Print this help. +-t: Test: No action is taken except to print the mv commands that would +be executed if -t was not given." +} + +while getopts "th" opt; do + case "$opt" in + t) echo=echo;; + h) phelp; exit 0;; + *) echo "$usage" 1>&2; exit 2;; + esac +done + +shift $((OPTIND - 1)) + +oldext=$1 +newext=$2 + +case $# in +[01]) echo -e "$usage\nUse -h for help." 1>&2; exit 2;; +2) shift ; shift; set -- *;; +*) shift ; shift;; +esac + +found= + +for file +do + case "$file" in + *$oldext) + newname="${file%$oldext}$newext" + $echo mv "$file" "$newname" + found=true;; + esac +done + +if [ -z "$found" ]; then + echo "No files ending in \"$oldext\"." + exit 1 +fi +exit 0 diff --git a/examples/scripts.v2/nmv b/examples/scripts.v2/nmv new file mode 100644 index 0000000..c91ba64 --- /dev/null +++ b/examples/scripts.v2/nmv @@ -0,0 +1,187 @@ +#! /bin/bash +# +# original from: +# @(#) ncp.ksh,nmv.ksh 1.1 94/07/23 +# 92/01/18 john h. dubois iii (john@armory.com) +# 92/01/31 added check for no args left after shifts +# 92/02/17 added help +# 92/02/25 remove path component from filename before tacking it onto dest. +# 92/03/15 exec mv or cp +# 93/07/13 Added -i +# 93/09/29 Made abort if file exists optional. +# 93/11/19 Exit before invoking mv if no files to move +# 94/01/03 Added o option +# 94/04/13 Added x option. +# Fixed appending of source filename, broken by earlier change. +# 94/07/23 Append only the filename part of the source path. +# +# conversion to bash v2 syntax done by Chet Ramey + +false() +{ + return 1 +} + +true() +{ + return 0 +} + +phelp() +{ +echo "$name: do a $cmd with extra checking and options. +$Usage +$name is used as a front end for $cmd to get the [icfo] options, and so +that a trailing / will force the last component of the path to be +interpreted as a directory, so that $name foo bar/ will fail if bar is +not an existing directory, instead of changing the name of foo to bar. +Effectively, $name foo bar/ is short for $name foo bar/foo +Options: +-h prints this help. +-c checks first for the existence of each file, and fails if it exists. +-i is like -c except that if the file exists and stdin and stdout are a + tty, a query is printed and a reply is read; a file is overwritten only + if the reply begins with 'y'. +-f unsets -c and -i (in case $cmd is aliased to $name). +-o (overwrite only) checks that the named file(s) exist and fails for any + that do not. It is the complement of the -c option. +Whichever of [cifo] comes later on the command line determines the behaviour. +Any of these options must come before any standard $cmd options." +} + +# interactive: Attempt to overwrite file should result in interactive +# query rather than automatic failure. +# noover: Do not overwrite files (if interactive is true, query, else fail) +# overwrite: Only overwriting is allowed, not creation of new files. +# debug: Print debugging info. +typeset interactive=false noover=false overwrite=false debug=false +name=${0##*/} + +case "$name" in +ncp|nmv) cmd=/bin/${name#?} ;; +*) echo "$name: Must be invoked as ncp or nmv." 1>&2 ; exit 2;; +esac + +Usage="Usage: $name [-cfhio] $cmd-cmd-line" + +while getopts :cfhiox opt; do + case $opt in + h) phelp; exit 0;; + x) debug=true ;; + c) noover=true ;; + i) noover=true ; interactive=true ;; + f) noover=false ; interactive=false ;; + o) overwrite=true ; noover=false ; interactive=false;; + +?) echo "$name: options should not be preceded by a '+'." 1>&2; exit 2;; + ?) echo "$name: $OPTARG: bad option. Use -h for help." 1>&2 ; exit 2;; + esac +done + +# remove args that were options +shift $((OPTIND - 1)) + +if [ $# -lt 2 ]; then + echo -e "$Usage\nUse -h for help." + exit +fi + +Check() +{ + if [ ! -f "$1" ] && $overwrite; then + echo "$name: $1: File does not exist." 1>&2 + return 1 + elif [ -f "$1" ] && $noover; then + if [ $interactive = false ] || [ ! -t 0 ] || [ ! -t 1 ]; then + echo "$name: $1: File exists." 1>&2 + return 1 + else + while :; do + echo -n \ +"$name: $1: File exists. Overwrite? (y)es/(n)o/(a)bort/(Y)es for all: " 1>&2 + read reply + case "$reply" in + y*) + echo "$name: Overwriting $1." + return 0 + ;; + Y*) + echo "$name: Overwriting $1." + interactive=false + noover=false + return 0 + ;; + [nN]*) + echo "$name: Skipping $2." + return 1 + ;; + [aA]*) + echo "$name: Aborting." + exit 1 + ;; + *) + echo "$name: Invalid response." 1>&2 + ;; + esac + done + fi + else + return 0 + fi +} + +# i is the index of the filename being examined +# lastarg is the index of the last filename before the dest directory name +typeset -i i=0 lastarg=$(($#-1)) + +# Sets argv[0..$#-1] +argv=("$@") +$debug && echo argv = "${argv[@]}" 1>&2 +dest=${argv[lastarg]} + +if $debug; then + echo \ +"interactive=$interactive noover=$noover overwrite=$overwrite debug=$debug +lastarg=$lastarg dest=$dest name=$name cmd=$cmd +files=$*" 1>&2 +fi + +if $noover || $overwrite; then + $debug && echo "checking for existance of directories..." 1>&2 + # If the destination is not intended to be a directory... + if [ $# -eq 2 ] && [ ! -d "$dest" ]; then + Check "$dest" "$1" || exit 0 # No files to copy + else + while [ $i -lt $lastarg ]; do + Check "$dest/${argv[i]##*/}" "${argv[i]}" || unset argv[i] + let i+=1 + done + fi +fi + +[ ${#argv[@]} -lt 2 ] && exit 0 + +# If only 2 args are given, mv/cp will not insist that the destination +# be a directory, which we want if the destination ends in "/" or if +# the original number of args was >2. +# $# is still the original number of args. +# Tack the file name onto the destination to force this behaviour. + +lastisslash() +{ + case "$1" in + */) return 0;; + *) return 1;; + esac +} + +if [ ${#argv[@]} = 2 ] && { lastisslash "$2" || [ $# -gt 2 ]; }; then + $debug && echo "Appending filename." 1>&2 + # Don't know which element of argv[] holds the source filename, + # since may have started with more than 1 source file & had some unset. + # So, compact args to make it easy to find the set one. + argv=("${argv[@]}") + argv[1]="${argv[1]}/${argv[0]##*/}" +fi + +$debug && echo "Executing command: $cmd ${argv[@]}" 1>&2 +exec $cmd "${argv[@]}" diff --git a/examples/scripts.v2/pages b/examples/scripts.v2/pages new file mode 100644 index 0000000..66ebc5f --- /dev/null +++ b/examples/scripts.v2/pages @@ -0,0 +1,187 @@ +#! /bin/bash +# +# original from: +# @(#) pages.sh 1.0 92/09/26 +# 92/09/05 John H. DuBois III (jhdiii@armory.com) +# 92/09/26 Added help +# +# conversion to bash v2 syntax by Chet Ramey + +Usage="$0 [-h] [-n lines/page] page-ranges [file ...]" + +usage() +{ + echo "$Usage" 1>&2 +} + +phelp() +{ +echo "$0: print selected pages. +Usage: $Usage + +If no file names are given, the standard input is read. + +The input is grouped into pages and a selected subset of them is printed. +Formfeeds are acted on correctly. + +If the output device does automatic line wrap, lines that longer than +the width of the output device will result in incorrect output. +The first non-option argument is a list of pages to print. + +Pages are given as a list of ranges separated by commas. +A range is either one number, two numbers separted by a dash, +or one number followed by a dash. A range consisting of one +number followed by a dash extends to the end of the document. + +Options: +-n sets the number of lines per page to n. The default is 66." +} + +while getopts "n:h" opt; do + case "$opt" in + n) LinesPerPage=$OPTARG;; + h) phelp; exit 0;; + *) usage; exit 2;; + esac +done + +shift $(($OPTIND - 1)) + +if [ $# -eq 0 ]; then + echo $0: no page ranges given. 1>&2 + usage + exit 1 +fi + +PageList=$1 +shift + +gawk " +BEGIN { + PageList = \"$PageList\"; LinesPerPage = \"$LinesPerPage\""' + if (LinesPerPage == "") + LinesPerPage = 66 + else + if (LinesPerPage !~ "[1-9][0-9]*") + ErrExit("Bad value for lines per page: " LinesPerPage) + LinesPerPage += 0 + NumRanges = split(PageList,Ranges,",") + for (i = 1; i <= NumRanges; i++) { + if ((StartRange = EndRange = Ranges[i]) !~ "^[0-9]+(-([0-9]+)?)?$") + ErrExit("Bad range \"" StartRange "\"") + sub("-.*","",StartRange) + sub(".*-","",EndRange) + if (EndRange == "") + EndRange = 2 ^ 30 + # Force StartRange and EndRange to be numeric values + if ((StartRange += 0) == 0 || (EndRange += 0) == 0) + ErrExit("Invalid page number \"0\" in range " Ranges[i]) + if (StartRange > EndRange) + ErrExit("Start page comes after end page in range " Ranges[i]) + TmpRangeStarts[i] = StartRange + TmpRangeEnds[i] = EndRange + } + + # Sort ranges + qsort(TmpRangeStarts,k) + RangeEnds[0] = 0 + for (i = 1; i <= NumRanges; i++) { + RangeEnds[i] = TmpRangeEnds[k[i]] + if ((RangeStarts[i] = TmpRangeStarts[k[i]]) <= RangeEnds[i - 1]) + ErrExit("Overlapping ranges: " Ranges[k[i]] "," Ranges[k[i - 1]]) + } + + RangeNum = LineNum = PageNum = 1 + InRange = In(PageNum,RangeStarts[RangeNum],RangeEnds[RangeNum]) + FS = "\014" +} + +{ + if (LineNum > LinesPerPage) + NewPage() + if (InRange) + printf "%s",$1 + # Deal with formfeeds + for (i = 2; i <= NF; i++) { + if (InRange) + printf "\014" + NewPage() + if (InRange) + printf "%s",$i + } + if (InRange) + print "" + LineNum++ +} + +function NewPage() { + PageNum++ + LineNum = 1 + # At the start of each page, check whether we are in a print range + WereInRange = InRange + InRange = In(PageNum,RangeStarts[RangeNum],RangeEnds[RangeNum]) + # If last page was in range and we no longer are, move to next range + if (WereInRange && !InRange && ++RangeNum > NumRanges) + exit +} + +function In(a,Min,Max) { + return (Min <= a && a <= Max) +} + +function ErrExit(S) { + print S > "/dev/stderr" + Err = 1 + exit 1 +} + +# Arr is an array of values with arbitrary indices. +# Array k is returned with numeric indices 1..n. +# The values in k are the indices of array arr, +# ordered so that if array arr is stepped through +# in the order arr[k[1]] .. arr[k[n]], it will be stepped +# through in order of the values of its elements. +# The return value is the number of elements in the array (n). +function qsort(arr,k, ArrInd,end) { + end = 0 + for (ArrInd in arr) + k[++end] = ArrInd; + qsortseg(arr,k,1,end); + return end +} + +function qsortseg(arr,k,start,end, left,right,sepval,tmp,tmpe,tmps) { + # handle two-element case explicitely for a tiny speedup + if ((end - start) == 1) { + if (arr[tmps = k[start]] > arr[tmpe = k[end]]) { + k[start] = tmpe + k[end] = tmps + } + return + } + left = start; + right = end; + sepval = arr[k[int((left + right) / 2)]] + # Make every element <= sepval be to the left of every element > sepval + while (left < right) { + while (arr[k[left]] < sepval) + left++ + while (arr[k[right]] > sepval) + right-- + if (left < right) { + tmp = k[left] + k[left++] = k[right] + k[right--] = tmp + } + } + if (left == right) + if (arr[k[left]] < sepval) + left++ + else + right-- + if (start < right) + qsortseg(arr,k,start,right) + if (left < end) + qsortseg(arr,k,left,end) +} +' "$@" diff --git a/examples/scripts.v2/pf b/examples/scripts.v2/pf new file mode 100644 index 0000000..ab6cd2c --- /dev/null +++ b/examples/scripts.v2/pf @@ -0,0 +1,127 @@ +#! /bin/bash +# +# original from: +# +# @(#) p.ksh 1.1 93/11/09 +# p: page compressed & plain files in the order given +# 92/01/23 john h. dubois iii (john@armory.com) +# 92/02/14 changed incorrect zpack to pcat +# 92/02/16 added help +# 92/10/11 search for file.Z and file.z if file not found +# 92/10/18 pass options to pager +# 93/11/09 Understand gzipped files too +# Wait after printing message about unreadable files +# Make less prompt include name of file being uncompressed +# +# conversion to bash v2 by Chet Ramey; renamed to pf +# +DefPager=/local/bin/less + +istrue() +{ + test 0 -ne "$1" +} + +warn() +{ + echo "$@" 1>&2 +} + +if [ "$1" = -h ]; then + echo \ +"$0: page a file. +Usage: $0 [pager-option ...] [filename ...] +Files are paged by the program specified in the user's PAGER +environment variable, or by $DefPager if PAGER is not set. +If no filename is given, text to page is read from the standard input. +If filenames are given, they are either paged directly, or unpacked/ +uncompressed and then paged. Files are assumed to be in packed, compressed, +or gzipped format if the filename ends in .Z, .z, or .gz respectively. +If a filename that does not end in .Z, .z, or .gz is not found, it is +searched for with one of those extensions attached. +Each group of plain files is paged by a single instance of the pager. +Each packed or compressed file is paged by a separate instance of the +pager. +Initial arguments beginning with + or - are taken to be pager options and +are passed to each instance of the pager. +If a pager option takes a value it should be given with the option as a +single argument (with no space between the option and the value)." + exit 0 +fi + +# Get pager options +while [ $# -gt 0 ]; do + case "$1" in + -*|+*) Opts="$Opts $1" ; shift;; + *) break;; + esac +done + +[ -z "$PAGER" ] && PAGER=$DefPager + +# Read from stdin +[ $# = 0 ] && exec $PAGER $Opts + +typeset -i filenum=0 badfile=0 + +for file; do + if [ ! -r "$file" ]; then + case "$file" in + *.[Zz]|*.gz) + # Check if user specified a compressed file without giving its extension + for ext in Z z gz; do + if [ -r "$file.$ext" ]; then + file="$file.$ext" + break + fi + done;; + esac + fi + if [ ! -r "$file" ]; then + warn "$file: cannot read." + badfile=1 + else + files[filenum]=$file + let filenum+=1 + fi +done + +if istrue $badfile && [ $filenum -gt 0 ]; then + echo -n "Press return to continue..." 1>&2 + read +fi + +unset plain + +for file in "${files[@]}"; do + case "$file" in + *.[zZ]|*.gz) + set -- Z zcat z pcat gz gzcat + # Find correct uncompression program + while [ $# -gt 0 ]; do + case "$file" in + *.$1) + # Page any uncompressed files so that they will be read + # in the correct order + [ ${#plain[@]} -gt 0 ] && $PAGER $Opts "${plain[@]}" + unset plain[*] + # If page is less, set the prompt to include the name of + # the file being uncompressed. Escape the . in the extension + # because less treats is specially in prompts (other dots + # in filenames will still be mucked with). + case "$PAGER" in + *less) Prompt="-P[${file%.$1}\\.$1] (%pb\\%)" ;; + *) unset Prompt ;; + esac + $2 "$file" | $PAGER "$Prompt" $Opts + break + esac + shift 2 + done + ;; + *) plain[${#plain[@]}]=$file;; + esac +done + +# Page any uncompressed files that haven't been paged yet +[ ${#plain[@]} -gt 0 ] && exec $PAGER $Opts "${plain[@]}" diff --git a/examples/scripts.v2/pmtop b/examples/scripts.v2/pmtop new file mode 100644 index 0000000..cc419ac --- /dev/null +++ b/examples/scripts.v2/pmtop @@ -0,0 +1,25 @@ +#! /bin/bash +# +# pmtop - poor man's `top' for SunOS 4.x +# + +CLEAR=clear # could also be 'tput clear' +HEADER="USER PID %CPU %MEM SZ RSS TT STAT START TIME COMMAND" + +if [ -n "$LINES" ]; then + SS=$(( $LINES - 2 )) +else + SS=20 +fi + +while : +do + $CLEAR + echo "$HEADER" + ps -aux | sort -nr -k 3 | sed ${SS}q + sleep 5 +done + +exit 0 + + diff --git a/examples/scripts.v2/ren b/examples/scripts.v2/ren new file mode 100644 index 0000000..da76026 --- /dev/null +++ b/examples/scripts.v2/ren @@ -0,0 +1,585 @@ +#!/bin/bash +#@ This program came from: ftp://ftp.armory.com/pub/scripts/ren +#@ Look there for the latest version. +#@ If you don't find it, look through http://www.armory.com/~ftp/ +# +# @(#) ren 2.1.1 2002-03-17 +# 1990-06-01 John H. DuBois III (john@armory.com) +# 1991-02-25 Improved help info +# 1992-06-07 Remove quotes from around shell pattern as required by new ksh +# 1994-05-10 Exit if no globbing chars given. +# 1995-01-23 Allow filename set to be given on command line. +# 1997-09-24 1.4 Let [] be used for globbing. Added x option. +# 1997-11-26 1.4.1 Notice if the sequences of globbing chars aren't the same. +# 1999-05-13 Changed name to ren to avoid conflict with /etc/rename +# 2000-01-01 1.4.2 Let input patterns that contain whitespace be used. +# 2001-02-14 1.5 Better test for whether old & new globbing seqs are identical. +# 2001-02-20 1.6 Added pP options. +# 2001-02-27 1.7 Added qf options. Improved interpretation of rename patterns. +# 2001-05-10 1.8 Allow multiple pP options. Added Qr options. +# 2001-07-25 2.0 Added mz options. +# 2001-11-25 2.1 Allow segment ranges to be given with -m. Work under ksh93. +# 2002-03-17 2.1.1 Fixed bug in test for legal expressions. + +# todo: It would be nice to be able to escape metacharacters with '\' +# todo: Should enhance patterns to make ] in a pair of brackets work ([]]) +# todo: Allow use of all ksh globbing patterns. +# todo: Allow use of extended regexps, with () to enumerate pieces and \num to +# todo: select them. +# +# Modifications for bash made by Chet Ramey <chet@po.cwru.edu> + +name=${0##*/} +Usage="Usage: +$name [-fhqtv] [-m<segstart[:segend]=operation>] [-z<len>] [-[pP]<pattern>] + oldpattern [newpattern [filename ...]] +or +$name -r [same options as above] oldpattern newpattern directory ..." +tell=false +verbose=false +warn=true +warnNoFiles=true +debug=false +recurse=false +inclPat= +exclPat= +declare -i inclCt=0 exclCt=0 +check=true +declare -i j op_end_seg + +# Begin bash additions +shopt -s extglob + +# +# ksh print emulation +# +# print [-Rnprsu[n]] [-f format] [arg ...] +# +# - end of options +# -R BSD-style -- only accept -n, no escapes +# -n do not add trailing newline +# -p no-op (no coprocesses) +# -r no escapes +# -s print to the history file +# -u n redirect output to fd n +# -f format printf "$format" "$@" +# + +print() +{ + local eflag=-e + local nflag= fflag= c + local fd=1 + + OPTIND=1 + while getopts "fRnprsu:" c + do + case $c in + R) eflag= ;; + r) eflag= ;; + n) nflag=-n ;; + s) sflag=y ;; + f) fflag=y ;; + u) fd=$OPTARG ;; + p) ;; + esac + done + shift $(( $OPTIND - 1 )) + + if [ -n "$fflag" ]; then + builtin printf "$@" >&$fd + return + fi + + case "$sflag" in + y) builtin history -s "$*" ;; + *) builtin echo $eflag $nflag "$@" >&$fd + esac +} + +# End bash additions + +while getopts :htvxp:P:fqQrm:z: opt; do + case $opt in + h) + print -r -- \ +"$name: rename files by changing parts of filenames that match a pattern. +$Usage +oldpattern and newpattern are subsets of sh filename patterns; the only +globbing operators (wildcards) allowed are ?, *, and []. All filenames that +match oldpattern will be renamed with the filename characters that match the +constant (non-globbing) characters of oldpattern changed to the corresponding +constant characters of newpattern. The characters of the filename that match +the globbing operators of oldpattern will be preserved. Globbing operators +in oldpattern must occur in the same order in newpattern; for every globbing +operators in newpattern there must be an identical globbing operators in +oldpattern in the same sequence. Both arguments should be quoted since +globbing operators are special to the shell. If filenames are given, only +those named are acted on; if not, all filenames that match oldpattern are acted +on. newpattern is required in all cases except when -m is given and no further +arguments are given. +If you are unsure whether a $name command will do what you intend, issue it +with the -t option first to be sure. +Examples: +$name \"/tmp/foo*.ba.?\" \"/tmp/new*x?\" + All filenames in /tmp that match foo*.ba.? will have the \"foo\" part + replaced by \"new\" and the \".ba.\" part replaced by \"x\". + For example, /tmp/fooblah.ba.baz would be renamed to /tmp/newblahxbaz. +$name \* \*- foo bar baz + foo, bar, and baz will be renamed to foo-, bar-, and baz-. +$name '????????' '????-??-??' + All filenames that are 8 characters long will be changed such that dashes + are inserted after the 4th and 6th characters. +Options: +-h: Print this help. +-r: Recursive operation. Filenames given on the command line after oldpattern + and newpattern are taken to be directories to traverse recursively. For + each subdirectory found, the specified renaming is applied to any matching + filenames. oldpattern and newpattern should not include any directory + components. +-p<pattern>, -P<pattern>: Act only on filenames that do (if -p is given) or do + not (if -P is given) match the sh-style filename globbing pattern + <pattern>. This further restricts the filenames that are acted on, beyond + the filename selection produced by oldpattern and the filename list (if + any). <pattern> must be quoted to prevent it from being interpreted by the + shell. Multiple instances of these options may be given. In this case, + filenames are acted on only if they match at least one of the patterns + given with -p and do not match any of the patterns given with -P. +-m<segstart[:segend]=operation>: For each file being renamed, perform a + mathematical operation on the string that results from concatenating + together the filename segments that matched globbing operator numbers + segstart through segend, where operators are numbered in order of + occurrence from the left. For example, in the pattern a?b*c[0-9]f, segment + 1 consists of the character that matched ?, segment 2 consists of the + character(s) that matched *, and segment 3 consists of the character that + matched [0-9]. The selected segments are replaced with the result of the + mathematical operation. + The concatenated string must consist of characters that can be interpreted + as a decimal integer; if it does not, the filename is not acted on. This + number is assigned to the variable 'i', which can be referenced by the + operation. The operations available are those understood by the ksh + interpreter, which includes most of the operators and syntax of the C + language. The original filename segment is replaced by the result of the + operation. If -m is used, newpattern may be an empty string or not given + at all (if no directory/file names are given). In this case, it is taken + to be the same as oldpattern. + If segend is given, any fixed text that occurs in the pattern between the + starting and ending globbing segments is discarded. If there are fewer + globbing segments than segend, no complaint is issued; the string is formed + from segment segstart through the last segment that does exist. + If segend is not given, the only segment acted on is startseg. + Examples: + $name -m3=i+6 '??*.ppm' + This is equivalent to: + $name -m3=i+6 '??*.ppm' '??*.ppm' + Since the old pattern and new pattern are identical, this would + normally be a no-op. But in this case, if a filename of ab079.ppm is + given, it is changed to ab85.ppm. + $name '-m1:2=i*2' 'foo??bar' + This will change a file named foo12bar to foo24bar + $name '-m1:2=i*2' 'foo?xyz?bar' + This will also change a file named foo1xyz2bar to foo24bar +-z<len>: Set the size of the number fields that result when -m is used. The + field is truncated to the trailing <len> digits or filled out to <len> + digits with leading zeroes. In the above example, if -z3 is given, the + output filename will be ab085.ppm. +-f: Force rename. By default, $name will not rename files if a file with the + new filename already exists. If -f is given, $name will carry out the + rename anyway. +-q: Quiet operation. By default, if -f is given, $name will still notify the + user if a rename results in replacement of an already-existing filename. + If -q is given, no notification is issued. +-Q: Suppress other warnings. By default, a warning is issued if no files are + selected for acting upon. If -Q is given, no warning is issued. +-v: Show the rename commands being executed. +-t: Show what rename commands would be done, but do not carry them out." + exit 0 + ;; + f) + check=false + ;; + q) + warn=false + ;; + Q) + warnNoFiles=false + ;; + r) + warnNoFiles=false + recurse=true + ;; + t) + tell=true + ;; + v) + verbose=true + ;; + x) + verbose=true + debug=true + ;; + p) + inclPats[inclCt]=$OPTARG + ((inclCt+=1)) + ;; + P) + exclPats[exclCt]=$OPTARG + ((exclCt+=1)) + ;; + m) + # Store operation for each segment number in ops[num] + # Store ending segment number in op_end_seg[num] + range=${OPTARG%%=*} + op=${OPTARG#*=} + start=${range%%:*} + end=${range#*:} + if [[ "$start" != +([0-9]) || "$start" -eq 0 ]]; then + print -ru2 -- "$name: Bad starting segment number given with -m: $start" + exit 1 + fi + if [[ "$end" != +([0-9]) || "$end" -eq 0 ]]; then + print -ru2 -- "$name: Bad ending segment number given with -m: $end" + exit 1 + fi + if [[ start -gt end ]]; then + print -ru2 -- "$name: Ending segment ($end) is less than starting segment ($start)" + exit 1 + fi + if [[ "$op" != @(|*[!_a-zA-Z0-9])i@(|[!_a-zA-Z0-9]*) ]]; then + print -ru2 -- \ + "$name: Operation given with -m does not reference 'i': $op" + exit 1 + fi + # Test whether operation is legal. let returns 1 both for error + # indication and when last expression evaluates to 0, so evaluate 1 + # after test expression. + i=1 + let "$op" 1 2>/dev/null || { + print -ru2 -- \ + "$name: Bad operation given with -m: $op" + exit 1 + } + ops[start]=$op + op_end_seg[start]=$end + ;; + z) + if [[ "$OPTARG" != +([0-9]) || "$OPTARG" -eq 0 ]]; then + print -ru2 -- "$name: Bad length given with -z: $OPTARG" + exit 1 + fi + typeset -Z$OPTARG j || exit 1 + ;; + +?) # no way to tell getopts to not treat +x as an option + print -r -u2 "$name: Do not prefix options with '+'." + exit 1 + ;; + :) + print -r -u2 \ +"$name: Option -$OPTARG requires a value. +$Usage +Use -h for help." + exit 1 + ;; + \?) + print -r -u2 \ +"$name: -$OPTARG: no such option. +$Usage +Use -h for help." + exit 1 + ;; + esac +done + +# remove args that were options +let OPTIND=OPTIND-1 +shift $OPTIND + +oldpat=$1 +newpat=$2 + +# If -m is given, a non-existant or null newpat should be set to oldpat +if [ ${#ops[*]} -gt 0 ]; then + case $# in + 0) + ;; + 1) + set -- "$oldpat" "$oldpat" + newpat=$oldpat + $debug && print -ru2 -- "Set new pattern to: $newpat" + ;; + *) + if [ -z "$newpat" ]; then + shift 2 + set -- "$oldpat" "$oldpat" "$@" + newpat=$oldpat + $debug && print -ru2 -- "Set new pattern to: $newpat" + fi + ;; + esac +fi + +# Make sure input patterns that contain whitespace can be expanded properly +IFS= + +origPat=$oldpat + +# Generate list of filenames to act on. +case $# in +[01]) + print -u2 "$Usage\nUse -h for help." + exit 1 + ;; +2) + if $recurse; then + print -r -u2 "$name: No directory names given with -r. Use -h for help." + exit 1 + fi + set -- $oldpat # Get list of all filenames that match 1st globbing pattern. + if [[ ! -a $1 ]]; then + $warnNoFiles && print -r -- "$name: No filenames match this pattern: $oldpat" + exit + fi + ;; +*) + shift 2 + ;; +esac + +integer patSegNum=1 numPatSegs + +# For old ksh +# while [[ "$oldpat" = *'[\*\?]'* ]]; do + +# Example oldpat: foo*.a +# Example newpat: bar*.b + +# Build list of non-pattern segments and globbing segments found in arguments. +# Note the patterns given are used to get the list of filenames to act on, +# to delimit constant segments, and to determine which parts of filenames are +# to be replaced. +# Examples given for first iteration (in the example, the only iteration) +# The || newpat is to ensure that new pattern does not have more globbing +# segments than old pattern +while [[ "$oldpat" = *@([\*\?]|\[+([!\]])\])* || + "$newpat" = *@([\*\?]|\[+([!\]])\])* ]]; do + ## Get leftmost globbing pattern in oldpat + + # Make r be oldpat with smallest left piece that includes a globbing + # pattern removed from it + r=${oldpat#*@([\*\?]|\[+([!\]])\])} # r=.a + # Make pat be oldpat with the above removed from it, leaving smallest + # left piece that includes a globbing pattern + pat=${oldpat%%"$r"} # pat=foo* + # Make l be pat with the globbing pattern removed from the right, + # leaving a constant string + l=${pat%@([\*\?]|\[+([!\]])\])} # l=foo + # Remove the constant part of pat from the left, leaving the globbing + # pattern + pat=${pat#"$l"} # pat=* + + # Do the same thing for newpat, solely to provide a reliable test that + # both oldpat & newpat contain exactly the same sequence of globbing + # patterns. + r=${newpat#*@([\*\?]|\[+([!\]])\])} # r=.b + npat=${newpat%%"$r"} # pat=bar* + l=${npat%@([\*\?]|\[+([!\]])\])} # l=bar + npat=${npat#"$l"} # npat=* + + if [[ "$pat" != "$npat" ]]; then + print -ru2 -- \ +"$name: Old-pattern and new-pattern do not have the same sequence of globbing chars. +Pattern segment $patSegNum: Old pattern: $pat New pattern: $npat" + exit 1 + fi + + ## Find parts before & after pattern + # oldpre[] stores the old constant part before the pattern, + # so that it can be removed and replaced with the new constant part. + oldpre[patSegNum]=${oldpat%%"$pat"*} # oldpre[1]=foo + # oldsuf stores the part that follows the globbing pattern, + # so that it too can be removed. + # After oldpre[] & oldsuf[] have been removed from a filename, what remains + # is the part matched by the globbing pattern, which is to be retained. + oldsuf[patSegNum]=${oldpat#*"$pat"} # oldsuf[1]=.a + # newpre[] stores the new constant part before the pattern, + # so that it can be used to replace the old constant part. + newpre[patSegNum]=${newpat%%"$pat"*} # newpre[1]=bar + # Get rid of processed part of patterns + oldpat=${oldpat#${oldpre[patSegNum]}"$pat"} # oldpat=.a + newpat=${newpat#${newpre[patSegNum]}"$pat"} # newpat=.b + # Store either * or ? in pats[], depending on whether this segment matches 1 + # or any number of characters. + [[ "$pat" = \[* ]] && pat=? + pats[patSegNum]=$pat + ((patSegNum+=1)) +done + +if [ patSegNum -eq 1 ]; then + print -u2 "No globbing chars in pattern." + exit 1 +fi + +oldpre[patSegNum]=${oldpat%%"$pat"*} # oldpre[2]=.a +oldsuf[patSegNum]=${oldpat#*"$pat"} # oldsuf[2]=.a +newpre[patSegNum]=${newpat%%"$pat"*} # newpre[2]=.b + +numPatSegs=patSegNum + +if $debug; then + patSegNum=1 + while [[ patSegNum -le numPatSegs ]]; do + print -ru2 -- \ +"Old prefix: <${oldpre[patSegNum]}> Old suffix: <${oldsuf[patSegNum]}> New prefix: <${newpre[patSegNum]}> Pattern: <${pats[patSegNum]}>" + ((patSegNum+=1)) + done +fi + +# Example filename: foox.a +# Example oldpat: foo*.a +# Example newpat: bar*.b + +integer numFiles=0 + +# Usage: renameFile filename [dirname] +# [dirname] is a directory name to prefix filenames with when they are printed +# for informational purposes. +# Uses globals: +# inclCt exclCt inclPats[] exclPats[] ops[] +# numPatSegs oldpre[] oldsuf[] newpre[] pats[] +# check warn tell verbose name +# Modifies globals: numFiles +function renameFile { + typeset file=$1 subdir=$2 + integer patSegNum patnum + typeset origname porigname newfile matchtext pnewfile matchsegs + integer startseg endseg + + origname=$file # origname=foox.a + porigname=$subdir$file + # Unfortunately, ksh88 does not do a good job of allowing for patterns + # stored in variables. Without the conditional expression being eval'ed, + # only sh patterns are recognized. If the expression is eval'ed, full + # ksh expressions can be used, but then expressions that contain whitespace + # break unless the user passed a pattern with the whitespace properly + # quoted, which is not intuititive. This is fixed in ksh93; full patterns + # work without being eval'ed. + if [ inclCt -gt 0 ]; then + patnum=0 + while [ patnum -lt inclCt ]; do + [[ "$file" = ${inclPats[patnum]} ]] && break + ((patnum+=1)) + done + if [ patnum -eq inclCt ]; then + $debug && print -ru2 -- "Skipping not-included filename '$porigname'" + return 1 + fi + fi + patnum=0 + while [ patnum -lt exclCt ]; do + if [[ "$file" = ${exclPats[patnum]} ]]; then + $debug && print -ru2 -- "Skipping excluded filename '$porigname'" + return 1 + fi + ((patnum+=1)) + done + # Extract matching segments from filename + ((numFiles+=1)) + patSegNum=1 + while [[ patSegNum -le numPatSegs ]]; do + # Remove a fixed prefix iteration: 1 2 + file=${file#${oldpre[patSegNum]}} # file=x.a file= + # Save the part of this suffix that is to be retained. To do this, we + # need to know what part of the suffix matched the current globbing + # segment. If the globbing segment is a *, this is done by removing + # the minimum part of the suffix that matches oldsuf (since * matches + # the longest segment possible). If the globbing segment is ? or [] + # (the latter has already been coverted to ?), it is done by taking the + # next character. + if [ "${pats[patSegNum]}" == \? ]; then + matchtext=${file#?} + matchtext=${file%$matchtext} + else + matchtext=${file%${oldsuf[patSegNum]}} # matchtext=x matchtext= + fi + $debug && print -ru2 -- "Matching segment $patSegNum: $matchtext" + file=${file#$matchtext} # file=.a file=.a + + matchsegs[patSegNum]=$matchtext + ((patSegNum+=1)) + done + + # Paste fixed and matching segments together to form new filename. + patSegNum=0 + newfile= + while [[ patSegNum -le numPatSegs ]]; do + matchtext=${matchsegs[patSegNum]} + startseg=patSegNum + if [ -n "${ops[startseg]}" ]; then + endseg=${op_end_seg[startseg]} + while [ patSegNum -lt endseg ]; do + ((patSegNum+=1)) + matchtext=$matchtext${matchsegs[patSegNum]} + done + if [[ "$matchtext" != +([-0-9]) ]]; then + print -ru2 -- \ +"Segment(s) $startseg - $endseg ($matchtext) of file '$porigname' do not form an integer; skipping this file." + return 2 + fi + i=$matchtext + let "j=${ops[startseg]}" || { + print -ru2 -- \ +"Operation failed on segment(s) $startseg - $endseg ($matchtext) of file '$file'; skipping this file." + return 2 + } + $debug && print -ru2 -- "Converted $matchtext to $j" + matchtext=$j + fi + newfile=$newfile${newpre[startseg]}$matchtext # newfile=barx newfile=barx.b + ((patSegNum+=1)) + done + + pnewfile=$subdir$newfile + if $check && [ -e "$newfile" ]; then + $warn && + print -ru2 -- "$name: Not renaming \"$porigname\"; destination filename \"$pnewfile\" already exists." + return 2 + fi + if $tell; then + print -n -r -- "Would move: $porigname -> $pnewfile" + $warn && [ -e "$newfile" ] && print -n -r " (destination filename already exists; would replace it)" + print "" + else + if $verbose; then + print -n -r -- "Moving: $porigname -> $pnewfile" + $warn && [ -e "$newfile" ] && print -n -r -- " (replacing old destination filename \"$pnewfile\")" + print "" + elif $warn && [ -e "$newfile" ]; then + print -r -- "$name: Note: Replacing old file \"$pnewfile\"" + fi + mv -f -- "$origname" "$newfile" + fi +} + +if $recurse; then + oPWD=$PWD + find "$@" -depth -type d ! -name '* +*' -print | while read dir; do + cd -- "$oPWD" + if cd -- "$dir"; then + for file in $origPat; do + renameFile "$file" "$dir/" + done + else + print -ru2 -- "$name: Could not access directory '$dir' - skipped." + fi + done +else + for file; do + renameFile "$file" + done +fi + +if [ numFiles -eq 0 ]; then + $warnNoFiles && print -ru2 -- \ + "$name: All filenames were excluded by patterns given with -p or -P." +fi diff --git a/examples/scripts.v2/rename b/examples/scripts.v2/rename new file mode 100644 index 0000000..96c46d6 --- /dev/null +++ b/examples/scripts.v2/rename @@ -0,0 +1,122 @@ +#! /bin/bash +# +# original from: +# @(#) rename.ksh 1.1 94/05/10 +# 90/06/01 John DuBois (spcecdt@armory.com) +# 91/02/25 Improved help info +# 92/06/07 remove quotes from around shell pattern as required by new ksh +# 94/05/10 Exit if no globbing chars given. +# +# conversion to bash v2 syntax by Chet Ramey + +phelp() +{ +echo "$usage +All files that match oldpattern will be renamed with the +filename components that match the constant parts of oldpattern +changed to the corresponding constant parts of newpattern. +The components of the filename that match variable parts of +oldpattern will be preserved. Variable parts in oldpattern +must occur in the same order in newpattern. Variables parts +can be '?' and '*'. +Example: +rename \"/tmp/foo*.ba.?\" \"/tmp/new*x?\" +All files in /tmp that match foo*.ba.? will have the \"foo\" part +replaced by \"new\" and the \".ba.\" part replaced by \"x\"." +} + +usage="usage: $name [-htv] oldpattern newpattern" +name=${0##/} + +while getopts "htv" opt; do + case "$opt" in + t) tell=true;; + v) verbose=true;; + h) phelp; exit 0;; + *) echo "$name: $usage" 1>&2; exit 2;; + esac +done +shift $((OPTIND - 1)) + +if [ $# -lt 2 ]; then + phelp + exit 2 +fi + +oldpat=$1 +newpat=$2 + +set -- $1 +if [ ! -e "$1" ]; then + echo "$name: no files match $oldpat." + exit 1 +fi + +typeset -i i=1 j + +# Example oldpat: foo*.a +# Example newpat: bar*.b + +# Examples given for first iteration (in the example, the only interation) +while :; do + case "$oldpat" in + *[\*\?]*) ;; + *) break;; + esac + + # Get leftmost globbing pattern in oldpat + pat=${oldpat#*[\*\?]} # pat=.a + pat=${oldpat%%"$pat"} # pat=foo* + pat=${pat##*[!\?\*]} # pat=* + # Find parts before & after pattern + oldpre[i]=${oldpat%%"$pat"*} # oldpre[1]=foo + oldsuf[i]=${oldpat#*"$pat"} # oldsuf[1]=.a + newpre[i]=${newpat%%"$pat"*} # newpre[1]=bar + # Get rid of processed part of patterns + oldpat=${oldpat#${oldpre[i]}"$pat"} # oldpat=.a + newpat=${newpat#${newpre[i]}"$pat"} # newpat=.b + let i=i+1 +done + +if [ $i -eq 1 ]; then + echo "No globbing chars in pattern." 1>&2 + exit 1 +fi + +oldpre[i]=${oldpat%%"$pat"*} # oldpre[2]=.a +oldsuf[i]=${oldpat#*"$pat"} # oldsuf[2]=.a +newpre[i]=${newpat%%"$pat"*} # newpre[2]=.b + +if [ -n "$verbose" ]; then + j=1 + while let "j < i"; do + echo \ +"Old prefix: ${oldpre[j]} Old suffix: ${oldsuf[j]} New prefix: ${newpre[j]}" + let j=j+1 + done +fi + +# Example file: foox.a + +for file; do + j=1 + origname=$file # origname=foox.a + newfile= + while let "j <= i"; do + # Peel off a prefix interation 1 2 + file=${file#${oldpre[j]}} # file=x.a file= + # Save the part of this prefix that is to be retained + const=${file%${oldsuf[j]}} # const=x const= + newfile=$newfile${newpre[j]}$const # newfile=barx newfile=barx.b + file=${file#$const} # file=.a file=.a + let j=j+1 + done + if [ -n "$tell" ]; then + echo "Would move \"$origname\" to \"$newfile\"." + else + if [ -n "$verbose" ]; then + echo "Moving \"$origname\" to \"$newfile\"." + fi + mv $origname $newfile + fi +done diff --git a/examples/scripts.v2/repeat b/examples/scripts.v2/repeat new file mode 100644 index 0000000..b6fccac --- /dev/null +++ b/examples/scripts.v2/repeat @@ -0,0 +1,121 @@ +#! /bin/bash +# +# original from: +# repeat: repeat a command. +# @(#) repeat.ksh 1.1 93/06/03 +# 90/05 john h. dubois iii (john@armory.com) +# 90/11 added help +# 93/06/03 Added s, h, p, and v options +# +# conversion to bash v2 syntax done by Chet Ramey + +istrue() +{ + test 0 -ne "$1" +} + +isfalse() +{ + test 0 -eq "$1" +} + +phelp() +{ +echo "$name: repeatedly execute a command line. +$Usage +commandline is executed once for each integer from startcount through endcount +inclusive. The default for startcount is 1 if a positive endcount or no +endcount is given, and -1 if a negative endcount is given. A count +parameter consisting of a single number is taken to be an endcount. If +only an endcount is given and it is positive, commandline is executed +endcount times. endcount may be less than startcount. If no endcount is +given (e.g. a count parameter of \"10-\"), commandline execution repeats +indefinitely with the iteration variable incrementing in a positive +direction. A count parameter of consisting of \"-\" will repeat +indefinitely starting with 1. + +Note that quoting and variables in commandline are interpreted twice, once +when it is passed to the repeat command, and once when it is actually executed. + +The iteration variable is \"count\". If \$count is used in commandline, make +sure it is quoted with ' or \. + +Options: +-h: Print this help. +-p: Print value of iteration variable on stderr before each iteration. +-s <sec>: sleep for <sec> seconds after each iteration except the last. +-v: Print start and end values before beginning." +} + +name=${0##*/} +Usage="Usage: repeat [-hpv] [-s <sec>] [[startcount]-][endcount] command [arg ...]" + +typeset -i count=1 forever=0 sleep=0 print=0 verbose=0 + +while getopts :0123456789hpvs: opt; do + case $opt in + h) phelp; exit 0;; + s) sleep=$OPTARG || exit 1;; + p) print=1;; + v)verbose=1;; + [0-9]) break;; + +?) echo "$name: options should not be preceded by a '+'." 1>&2; exit 2;; + ?) echo "$name: $OPTARG: bad option. Use -h for help." 1>&2; exit 2;; + esac +done + +# remove args that were options +shift $((OPTIND-1)) + +if [ $# -lt 2 ]; then + echo -e "$Usage\nUse -h for help." 1>&2 + exit 2 +fi + +case "$1" in +-[0-9]*-|[0-9]*-) + # Start value only + count=${1%-} + forever=1 + end="-1"; + ;; +-[0-9]*-[0-9]*|[0-9]*-[0-9]*) + # Start and end value + s=${1%-} + end=${s##[0-9]*-} + count=${s%-$end} + ;; +-[0-9]*|[0-9]*) + end=$1 + case "$end" in + -\*) count=-1;; + esac + ;; +-) + forever=1 + end="-1"; + ;; +*) + echo "$name: bad count parameter: $1" 1>&2 + exit 1 + ;; +esac + +shift + +[ -z "$end" ] && [ $count -le "$end" ] && increment=1 || increment=-1 + +istrue $verbose && echo "start=$count end=$end" 1>&2 + +# Need to do this here so that up to this point, -0 will keep the leading - +# and end will not be 0 if no value assigned +typeset -i end + +let end+=increment # make loop inclusive of original endcount + +while istrue $forever || [ $count -ne $end ]; do + istrue $print && echo $count 1>&2 + eval "$@" + istrue $sleep && sleep $sleep + let count+=increment +done diff --git a/examples/scripts.v2/shprof b/examples/scripts.v2/shprof new file mode 100644 index 0000000..73a1bb9 --- /dev/null +++ b/examples/scripts.v2/shprof @@ -0,0 +1,66 @@ +#! /bin/bash +# +# shprof - a line profiler for shell scripts +# +# adapted from a similar program included in `The New KornShell' by +# Bolsky and Korn and posted to usenet by bsh20858@challenger.fhda.edu +# +# converted to bash v2 syntax by Chet Ramey +# +TMPFILE=${TMP:-/tmp}/shprof$$ + +trap 'rm -f $TMPFILE' EXIT + +errexit() +{ + echo $0: "$@" >&2 + exit 1 +} + +# create script with profiling enabled +cat > $TMPFILE <<- \_EOF_ + declare -a _line + _profend() + { + case "$1" in + /*|./*) file="$1" ;; + *) file=$(type -path "$1") ;; + esac + + echo "*** line profile for $file ***" + i=1; + while read -r && [ $i -le $NLINE ]; do + count=${_line[$i]} + if [ "$count" -gt 0 ]; then + echo "[$count] $i: $REPLY" + fi + i=$((i + 1)) + done <$file +_EOF_ +# make the profiling script remove itself after printing line stats +echo "rm -f $TMPFILE" >> $TMPFILE +cat >> $TMPFILE <<- \_EOF_ + } + _command=$1 + shift + i=1 + NLINE=$(wc -l < "$_command") + while [ $i -le $NLINE ]; do + _line[$i]=0 + i=$((i + 1)) + done + unset i + trap "_profend ${_command}" EXIT + trap '_line[$LINENO]=$((${_line[$LINENO]} + 1))' DEBUG + LINENO=0 +_EOF_ + +case "$1" in +/*|./*) file=$1 ;; +*) file=$((type -path "$1")) ;; +esac + +cat "${file-$1}" >> $TMPFILE || errexit "${1}: cannot open" +chmod +x $TMPFILE + +exec -a "$file" $TMPFILE "$@" diff --git a/examples/scripts.v2/untar b/examples/scripts.v2/untar new file mode 100644 index 0000000..1ba6b6b --- /dev/null +++ b/examples/scripts.v2/untar @@ -0,0 +1,80 @@ +#! /bin/bash +# +# original from: +# @(#) untar.ksh 1.0 93/11/10 +# 92/10/08 john h. dubois iii (john@armory.com) +# 92/10/31 make it actually work if archive isn't in current dir! +# 93/11/10 Added pack and gzip archive support +# +# conversion to bash v2 syntax done by Chet Ramey + +phelp() +{ +echo \ +"$name: extract tar archives into directories, uncompressing if neccessary. +Usage: $name archive[.tar[.[Z|gz]]] .. +If an archive name given does not end in .tar, .tar.Z, or .tar.gz, it is +searched for first with .tar added, then .tar.Z, and then .tar.gz added. +The real filename must end in either .tar, .tar.Z, or .tar.gz. A +directory with the name of the archive is created in the current directory +(not necessarily the directory that the archive is in) if it does not +exist, and the the contents of the archive are extracted into it. +Absolute pathnames in tarfiles are suppressed." +} + +if [ $# -eq 0 ]; then + phelp + exit 1 +fi + +name=${0##/} +OWD=$PWD + +for file; do + cd $OWD + case "$file" in + *.tar.Z) ArchiveName=${file%%.tar.Z} zcat=zcat;; + *.tar.z) ArchiveName=${file%%.tar.z} zcat=pcat;; + *.tar.gz) ArchiveName=${file%%.tar.gz} zcat=gzcat;; + *) ArchiveName=$file + for ext in "" .Z .z .gz; do + if [ -f "$file.tar$ext" ]; then + file="$file.tar$ext" + break + fi + done + if [ ! -f "$file" ]; then + echo "$file: cannot find archive." 1>&2 + continue + fi + ;; + esac + if [ ! -r "$file" ]; then + echo "$file: cannot read." >&2 + continue + fi + DirName=${ArchiveName##*/} + [ -d "$DirName" ] || { + mkdir "$DirName" || { + echo "$DirName: could not make archive directory." 1>&2 + continue + } + } + + cd $DirName || { + echo "$name: cannot cd to $DirName" 1>&2 + continue + } + + case "$file" in + /*) ;; + *) file=$OWD/$file ;; + esac + + echo "Extracting archive $file into directory $DirName..." + case "$file" in + *.tar.Z|*.tar.z|*.tar.gz) $zcat $file | tar xvf -;; + *.tar) tar xvf $file;; + esac + echo "Done extracting archive $file into directory $DirName." +done diff --git a/examples/scripts.v2/uudec b/examples/scripts.v2/uudec new file mode 100644 index 0000000..7984058 --- /dev/null +++ b/examples/scripts.v2/uudec @@ -0,0 +1,45 @@ +: +# @(#) uudec.sh 1.0 93/11/22 +# 92/08/04 john@armory.com (John H. DuBois III) +# 93/11/22 Added help. + +isfalse() +{ + test 0 -eq "$1" +} + +phelp() +{ +"$name: process uuencoded files. +Usage: uudec [-h] filename ... +Options: +-h: Print this help." +} + +name=${0##*/} + +typeset -i force=0 + +while getopts "hf" opt; do + case "$opt" in + h) phelp; exit 0;; + f) force=1;; + *) echo "$Usage" 1>&2; exit 2;; + esac +done + +shift $((OPTIND - 1)) + +for file; do + echo "$file" + while read b mode filename && [ "$b" != begin ]; do :; done < "$file" + if [ "$b" = begin ]; then + if [ -f "$filename" ] && isfalse $force; then + echo "Output file \"$filename\" exists. Not written." + else + uudecode "$file" + fi + else + echo "No begin line." + fi +done diff --git a/examples/scripts.v2/uuenc b/examples/scripts.v2/uuenc new file mode 100644 index 0000000..480aa48 --- /dev/null +++ b/examples/scripts.v2/uuenc @@ -0,0 +1,69 @@ +#! /bin/bash +# +# original from: +# @(#) uuenc.ksh 1.0 93/09/18 +# 93/09/18 john h. dubois iii (john@armory.com) +# +# conversion to bash v2 syntax by Chet Ramey + +istrue() +{ + test 0 -ne "$1" +} + +isfalse() +{ + test 0 -eq "$1" +} + +phelp() +{ +echo "$name: uuencode files. +$Usage +For each filename given, $name uuencodes the file, using the final +component of the file's path as the stored filename in the uuencoded +archive and, with a .${SUF} appended, as the name to store the archive in. +Example: +$name /tmp/foo +The file /tmp/foo is uuencoded, with \"foo\" stored as the name to uudecode +the file into, and the output is stored in a file in the current directory +with the name \"foo.${SUF}\". +Options: +-f: Normally, if the file the output would be stored in already exists, + it is not overwritten and an error message is printed. If -f (force) + is given, it is silently overwritten. +-h: Print this help." +} + +name=${0##*/} +Usage="Usage: $name [-hf] <filename> ..." +typeset -i force=0 + +SUF=uu + +while getopts :hf opt; do + case $opt in + h) phelp; exit 0;; + f) force=1;; + +?) echo "$name: options should not be preceded by a '+'." 1>&2 ; exit 2;; + ?) echo "$name: $OPTARG: bad option. Use -h for help." 1>&2 ; exit 2;; + esac +done + +# remove args that were options +shift $((OPTIND - 1)) + +if [ $# -lt 1 ]; then + echo "$Usage\nUse -h for help." 1>&2 + exit +fi + +for file; do + tail=${file##*/} + out="$tail.${SUF}" + if isfalse $force && [ -a "$out" ]; then + echo "$name: $out: file exists. Use -f to overwrite." 1>&2 + else + uuencode $file $tail > $out + fi +done diff --git a/examples/scripts.v2/vtree b/examples/scripts.v2/vtree new file mode 100644 index 0000000..7523cc8 --- /dev/null +++ b/examples/scripts.v2/vtree @@ -0,0 +1,137 @@ +#! /bin/bash +# +# original from: +# vtree: visual directory tree +# @(#) vtree.sh 1.1 91/07/01 +# 90/04 john h. dubois iii (john@armory.com) +# 91/07/01 fixed bug that caused problems when dir given on command line, +# added some info to help, changed to 4-space indenting +# +# conversion to bash v2 syntax done by Chet Ramey +# +help=\ +"Syntax: vtree [startdir] [namelen=#] [linelen=#] +If startdir is not specified, tree will start at current dir. + +namelen specifies the minimum number of characters of a directory name that +are guaranteed to be printed. +This is a tradeoff between the number of tree levels that can fit on a +screen line and the number of chars of each dir name that can be printed. +In most cases it will be possible to print more than namelen characters of +the name (a name up to namelen+1 chars will always be printed in full), +but in some cases truncation down to namelen chars will occur. +If truncation occurs, a '>' is printed at the end of the name. +namelen=8 (the default) typically causes about 5 dirs/1000 to be truncated. +namelen=7 typically causes about 10 dirs/1000 to be truncated. +namelen=8 will allow 6 full length dirs to be printed in 79 columns. +namelen=7 will allow 7 full length dirs to be printed in 79 columns; + +linelen specifies the maximum number of characters to print on one screen +line. All characters beyond this are truncated. The default is 1024. +To avoid line wrap on an 80 column terminal with autowrap, use linelen=79. +" + +for i in "$@"; do + case $i in + -h) echo "$help"; exit;; + *=*) + vars="$vars $i" + ;; + *) + if [ ! -x $i ] || [ ! -d $i ]; then # arg must be a dir and executable + echo "$i: directory not accessible." + exit + fi + cd $i + ;; + esac + shift +done + +pwd # print path of root of tree + +# find all directories depth first; ignore permission errors +find . -type d -print 2> /dev/null | \ +gawk -F/ ' + +# Do this block for NR == 1 instead of BEGIN because command line var +# assignments are not done until after BEGIN block is executed. +NR == 1 { + if (namelen) + MaxLen = namelen; + else + MaxLen = 8; + if (!linelen) + linelen = 1024 + HSpace = substr(" ",1,MaxLen); # used to indent tree + n = 0; # number of dirs found on one major branch +} + +$0 != "." { # do for every line produced by find except tree root dir + if (NF == 2 && n > 0) # print major branch whenever a new one starts + list(); + Depth[n] = NF - 1; # record depth and name of dir + Name[n++] = $NF; +} + +END { + list() # print last major branch +} + +function list() { + Line = Name[0]; # initialize first line of branch to be branch base + for (i = 1; i < n; i++) { # for each name in major branch + if (Depth[i] == Depth[i-1] + 1) + AddHLink(); # if moving deeper into branch, use same line + else { + print substr(Line,1,linelen); # last line is done; print it + Line = ""; # start new line + # print indentation, vert links, and vert/horiz links + for (d = 1; d < Depth[i] - 1; d++) # for each level of indentation + # if a vert. link has been established for this level + if (VLink[d]) + Line = Line HSpace " | "; + else # print empty indentation + Line = Line HSpace " "; + # Print last part of vert. link + if (VLink[d] == i) { + VLink[d] = 0; # mark level for no vert link + Line = Line HSpace " \\--"; + } + else + Line = Line HSpace " |--"; + } + Line = Line Name[i]; # Add dir name to line + } + print substr(Line,1,linelen); # print last line of major branch + n = 0; # reset name counter +} + +function AddHLink() { + NDepth = Depth[i]; # Depth of this name + VLink[NDepth - 1] = 0; + # search until a name found at a level less than this one + for (j = i + 1; j < n && Depth[j] >= NDepth; j++) + # keep track of last name that VLink should connect to + if (Depth[j] == NDepth) + VLink[NDepth - 1] = j; + if (VLink[NDepth - 1]) { + NLine = substr(Line,1,(NDepth - 2) * (MaxLen + 4) + MaxLen + 1); + if (length(NLine) < length(Line)) + Line = substr(NLine,1,length(NLine) - 1) ">" + else + Line = NLine; + Line = Line substr("--------------+--", + 18 - ((NDepth - 1) * (MaxLen + 4) - length(Line))); + } + else { + NLine = substr(Line,1,(NDepth - 2) * (MaxLen + 4) + MaxLen + 3); + if (length(NLine) < length(Line)) + Line = substr(NLine,1,length(NLine) - 1) ">" + else + Line = NLine; + Line = Line substr("-----------------", + 1,(NDepth - 1) * (MaxLen + 4) - length(Line)); + } +} +' $vars diff --git a/examples/scripts.v2/where b/examples/scripts.v2/where new file mode 100644 index 0000000..7a1dbde --- /dev/null +++ b/examples/scripts.v2/where @@ -0,0 +1,111 @@ +#! /bin/bash +# +# original from: +# @(#) where.ksh 1.1 94/07/11 +# 91/01/12 john h. dubois iii (john@armory.com) +# 92/08/10 Only print executable *files*. +# 92/10/06 Print err msg if no match found. +# 92/11/27 Added implicit * +# 93/07/23 Print help only if -h is given. +# 94/01/01 Added -x option +# 94/07/11 Don't bother with eval +# +# conversion to bash v2 syntax done by Chet Ramey + +name=${0##*/} +Usage="Usage: $name [-hx] 'pattern' ..." +typeset -i exact=0 + +phelp() +{ +echo "$name: find executable files in PATH that match patterns. +$Usage +$name searches each directory specified in the PATH environment variable +for executable files that match the specified patterns. Patterns are +given as Korn shell filename patterns. They are surrounded by implicit +'*' characters, so that \"foo\" will match any executble file whose name +contains contains \"foo\". This can be overridden by using '^' and '$' to +force a match to start at the beginning and end at the end of a filename +respectively. Characters that are special to the shell must generally +be protected from the shell by surrounding them with quotes. +Examples: +$name foo +lists all executable files in PATH that contain foo. +$name '^b*sh$' +lists all executable files in PATH that start with b and end with sh. +An error message is printed if a no matching file is found for a pattern. +Options: +-h: Print this help. +-x: Find exact matches only; equivalent to putting ^ and $ at the start + and end of each pattern." +} + +istrue() +{ + test 0 -ne "$1" +} + +isfalse() +{ + test 0 -eq "$1" +} + +while getopts "xh" opt; do + case "$opt" in + x) exact=1;; + h) phelp ; exit 0;; + *) echo -e "$Usage\nUse -h for help." 1>&2; exit 2;; + esac +done + +shift $((OPTIND-1)) + +set +f # make sure filename globbing is on +Args=("$@") # save args + +OIFS=$IFS +IFS=: # Make PATH be split on : +Paths=($PATH) +IFS=$OIFS + +for arg in "${Args[@]}"; do + + # get rid of leading ^ + if istrue $exact; then + arg=${arg} + else + case "$arg" in + ^*) arg=${arg#?};; + *) arg="*$arg" ;; # Pattern is not anchored at start + esac + fi + + # get rid of trailing $ + if istrue $exact; then + arg="$arg" + else + case "$arg" in + *\$) arg=${arg%?} ;; + *) arg="$arg*" ;; + esac + fi + + found=0 # Pattern not found yet + Patterns= + # Make a pattern for each element of PATH + for PathElem in "${Paths[@]}"; do + [ -z "$PathElem" ] && PathElem=. + Patterns="$Patterns $PathElem/$arg" + done + + # Find all pattern matches that are executable regular files. + for file in $Patterns; do + if [ -x "$file" ] && [ -f "$file" ]; then + echo "$file" + found=1 + fi + done + if [ $found = 0 ]; then + echo "$arg: not found." 1>&2 + fi +done |