From 772f20abb0a3a0979c440114bf3a1cff5b3cef03 Mon Sep 17 00:00:00 2001 From: cvpcs Date: Wed, 2 Jun 2010 11:02:31 -0500 Subject: initial import of bash 4.1 --- examples/scripts.v2/ren | 585 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 585 insertions(+) create mode 100644 examples/scripts.v2/ren (limited to 'examples/scripts.v2/ren') 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 + +name=${0##*/} +Usage="Usage: +$name [-fhqtv] [-m] [-z] [-[pP]] + 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, -P: Act only on filenames that do (if -p is given) or do + not (if -P is given) match the sh-style filename globbing pattern + . This further restricts the filenames that are acted on, beyond + the filename selection produced by oldpattern and the filename list (if + any). 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: 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: Set the size of the number fields that result when -m is used. The + field is truncated to the trailing digits or filled out to + 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 -- cgit v1.1