1 | #!/bin/bash
|
---|
2 | #@ This program came from: ftp://ftp.armory.com/pub/scripts/ren
|
---|
3 | #@ Look there for the latest version.
|
---|
4 | #@ If you don't find it, look through http://www.armory.com/~ftp/
|
---|
5 | #
|
---|
6 | # @(#) ren 2.1.1 2002-03-17
|
---|
7 | # 1990-06-01 John H. DuBois III (john@armory.com)
|
---|
8 | # 1991-02-25 Improved help info
|
---|
9 | # 1992-06-07 Remove quotes from around shell pattern as required by new ksh
|
---|
10 | # 1994-05-10 Exit if no globbing chars given.
|
---|
11 | # 1995-01-23 Allow filename set to be given on command line.
|
---|
12 | # 1997-09-24 1.4 Let [] be used for globbing. Added x option.
|
---|
13 | # 1997-11-26 1.4.1 Notice if the sequences of globbing chars aren't the same.
|
---|
14 | # 1999-05-13 Changed name to ren to avoid conflict with /etc/rename
|
---|
15 | # 2000-01-01 1.4.2 Let input patterns that contain whitespace be used.
|
---|
16 | # 2001-02-14 1.5 Better test for whether old & new globbing seqs are identical.
|
---|
17 | # 2001-02-20 1.6 Added pP options.
|
---|
18 | # 2001-02-27 1.7 Added qf options. Improved interpretation of rename patterns.
|
---|
19 | # 2001-05-10 1.8 Allow multiple pP options. Added Qr options.
|
---|
20 | # 2001-07-25 2.0 Added mz options.
|
---|
21 | # 2001-11-25 2.1 Allow segment ranges to be given with -m. Work under ksh93.
|
---|
22 | # 2002-03-17 2.1.1 Fixed bug in test for legal expressions.
|
---|
23 |
|
---|
24 | # todo: It would be nice to be able to escape metacharacters with '\'
|
---|
25 | # todo: Should enhance patterns to make ] in a pair of brackets work ([]])
|
---|
26 | # todo: Allow use of all ksh globbing patterns.
|
---|
27 | # todo: Allow use of extended regexps, with () to enumerate pieces and \num to
|
---|
28 | # todo: select them.
|
---|
29 | #
|
---|
30 | # Modifications for bash made by Chet Ramey <chet@po.cwru.edu>
|
---|
31 |
|
---|
32 | name=${0##*/}
|
---|
33 | Usage="Usage:
|
---|
34 | $name [-fhqtv] [-m<segstart[:segend]=operation>] [-z<len>] [-[pP]<pattern>]
|
---|
35 | oldpattern [newpattern [filename ...]]
|
---|
36 | or
|
---|
37 | $name -r [same options as above] oldpattern newpattern directory ..."
|
---|
38 | tell=false
|
---|
39 | verbose=false
|
---|
40 | warn=true
|
---|
41 | warnNoFiles=true
|
---|
42 | debug=false
|
---|
43 | recurse=false
|
---|
44 | inclPat=
|
---|
45 | exclPat=
|
---|
46 | declare -i inclCt=0 exclCt=0
|
---|
47 | check=true
|
---|
48 | declare -i j op_end_seg
|
---|
49 |
|
---|
50 | # Begin bash additions
|
---|
51 | shopt -s extglob
|
---|
52 |
|
---|
53 | #
|
---|
54 | # ksh print emulation
|
---|
55 | #
|
---|
56 | # print [-Rnprsu[n]] [-f format] [arg ...]
|
---|
57 | #
|
---|
58 | # - end of options
|
---|
59 | # -R BSD-style -- only accept -n, no escapes
|
---|
60 | # -n do not add trailing newline
|
---|
61 | # -p no-op (no coprocesses)
|
---|
62 | # -r no escapes
|
---|
63 | # -s print to the history file
|
---|
64 | # -u n redirect output to fd n
|
---|
65 | # -f format printf "$format" "$@"
|
---|
66 | #
|
---|
67 |
|
---|
68 | print()
|
---|
69 | {
|
---|
70 | local eflag=-e
|
---|
71 | local nflag= fflag= c
|
---|
72 | local fd=1
|
---|
73 |
|
---|
74 | OPTIND=1
|
---|
75 | while getopts "fRnprsu:" c
|
---|
76 | do
|
---|
77 | case $c in
|
---|
78 | R) eflag= ;;
|
---|
79 | r) eflag= ;;
|
---|
80 | n) nflag=-n ;;
|
---|
81 | s) sflag=y ;;
|
---|
82 | f) fflag=y ;;
|
---|
83 | u) fd=$OPTARG ;;
|
---|
84 | p) ;;
|
---|
85 | esac
|
---|
86 | done
|
---|
87 | shift $(( $OPTIND - 1 ))
|
---|
88 |
|
---|
89 | if [ -n "$fflag" ]; then
|
---|
90 | builtin printf "$@" >&$fd
|
---|
91 | return
|
---|
92 | fi
|
---|
93 |
|
---|
94 | case "$sflag" in
|
---|
95 | y) builtin history -s "$*" ;;
|
---|
96 | *) builtin echo $eflag $nflag "$@" >&$fd
|
---|
97 | esac
|
---|
98 | }
|
---|
99 |
|
---|
100 | # End bash additions
|
---|
101 |
|
---|
102 | while getopts :htvxp:P:fqQrm:z: opt; do
|
---|
103 | case $opt in
|
---|
104 | h)
|
---|
105 | print -r -- \
|
---|
106 | "$name: rename files by changing parts of filenames that match a pattern.
|
---|
107 | $Usage
|
---|
108 | oldpattern and newpattern are subsets of sh filename patterns; the only
|
---|
109 | globbing operators (wildcards) allowed are ?, *, and []. All filenames that
|
---|
110 | match oldpattern will be renamed with the filename characters that match the
|
---|
111 | constant (non-globbing) characters of oldpattern changed to the corresponding
|
---|
112 | constant characters of newpattern. The characters of the filename that match
|
---|
113 | the globbing operators of oldpattern will be preserved. Globbing operators
|
---|
114 | in oldpattern must occur in the same order in newpattern; for every globbing
|
---|
115 | operators in newpattern there must be an identical globbing operators in
|
---|
116 | oldpattern in the same sequence. Both arguments should be quoted since
|
---|
117 | globbing operators are special to the shell. If filenames are given, only
|
---|
118 | those named are acted on; if not, all filenames that match oldpattern are acted
|
---|
119 | on. newpattern is required in all cases except when -m is given and no further
|
---|
120 | arguments are given.
|
---|
121 | If you are unsure whether a $name command will do what you intend, issue it
|
---|
122 | with the -t option first to be sure.
|
---|
123 | Examples:
|
---|
124 | $name \"/tmp/foo*.ba.?\" \"/tmp/new*x?\"
|
---|
125 | All filenames in /tmp that match foo*.ba.? will have the \"foo\" part
|
---|
126 | replaced by \"new\" and the \".ba.\" part replaced by \"x\".
|
---|
127 | For example, /tmp/fooblah.ba.baz would be renamed to /tmp/newblahxbaz.
|
---|
128 | $name \* \*- foo bar baz
|
---|
129 | foo, bar, and baz will be renamed to foo-, bar-, and baz-.
|
---|
130 | $name '????????' '????-??-??'
|
---|
131 | All filenames that are 8 characters long will be changed such that dashes
|
---|
132 | are inserted after the 4th and 6th characters.
|
---|
133 | Options:
|
---|
134 | -h: Print this help.
|
---|
135 | -r: Recursive operation. Filenames given on the command line after oldpattern
|
---|
136 | and newpattern are taken to be directories to traverse recursively. For
|
---|
137 | each subdirectory found, the specified renaming is applied to any matching
|
---|
138 | filenames. oldpattern and newpattern should not include any directory
|
---|
139 | components.
|
---|
140 | -p<pattern>, -P<pattern>: Act only on filenames that do (if -p is given) or do
|
---|
141 | not (if -P is given) match the sh-style filename globbing pattern
|
---|
142 | <pattern>. This further restricts the filenames that are acted on, beyond
|
---|
143 | the filename selection produced by oldpattern and the filename list (if
|
---|
144 | any). <pattern> must be quoted to prevent it from being interpreted by the
|
---|
145 | shell. Multiple instances of these options may be given. In this case,
|
---|
146 | filenames are acted on only if they match at least one of the patterns
|
---|
147 | given with -p and do not match any of the patterns given with -P.
|
---|
148 | -m<segstart[:segend]=operation>: For each file being renamed, perform a
|
---|
149 | mathematical operation on the string that results from concatenating
|
---|
150 | together the filename segments that matched globbing operator numbers
|
---|
151 | segstart through segend, where operators are numbered in order of
|
---|
152 | occurrence from the left. For example, in the pattern a?b*c[0-9]f, segment
|
---|
153 | 1 consists of the character that matched ?, segment 2 consists of the
|
---|
154 | character(s) that matched *, and segment 3 consists of the character that
|
---|
155 | matched [0-9]. The selected segments are replaced with the result of the
|
---|
156 | mathematical operation.
|
---|
157 | The concatenated string must consist of characters that can be interpreted
|
---|
158 | as a decimal integer; if it does not, the filename is not acted on. This
|
---|
159 | number is assigned to the variable 'i', which can be referenced by the
|
---|
160 | operation. The operations available are those understood by the ksh
|
---|
161 | interpreter, which includes most of the operators and syntax of the C
|
---|
162 | language. The original filename segment is replaced by the result of the
|
---|
163 | operation. If -m is used, newpattern may be an empty string or not given
|
---|
164 | at all (if no directory/file names are given). In this case, it is taken
|
---|
165 | to be the same as oldpattern.
|
---|
166 | If segend is given, any fixed text that occurs in the pattern between the
|
---|
167 | starting and ending globbing segments is discarded. If there are fewer
|
---|
168 | globbing segments than segend, no complaint is issued; the string is formed
|
---|
169 | from segment segstart through the last segment that does exist.
|
---|
170 | If segend is not given, the only segment acted on is startseg.
|
---|
171 | Examples:
|
---|
172 | $name -m3=i+6 '??*.ppm'
|
---|
173 | This is equivalent to:
|
---|
174 | $name -m3=i+6 '??*.ppm' '??*.ppm'
|
---|
175 | Since the old pattern and new pattern are identical, this would
|
---|
176 | normally be a no-op. But in this case, if a filename of ab079.ppm is
|
---|
177 | given, it is changed to ab85.ppm.
|
---|
178 | $name '-m1:2=i*2' 'foo??bar'
|
---|
179 | This will change a file named foo12bar to foo24bar
|
---|
180 | $name '-m1:2=i*2' 'foo?xyz?bar'
|
---|
181 | This will also change a file named foo1xyz2bar to foo24bar
|
---|
182 | -z<len>: Set the size of the number fields that result when -m is used. The
|
---|
183 | field is truncated to the trailing <len> digits or filled out to <len>
|
---|
184 | digits with leading zeroes. In the above example, if -z3 is given, the
|
---|
185 | output filename will be ab085.ppm.
|
---|
186 | -f: Force rename. By default, $name will not rename files if a file with the
|
---|
187 | new filename already exists. If -f is given, $name will carry out the
|
---|
188 | rename anyway.
|
---|
189 | -q: Quiet operation. By default, if -f is given, $name will still notify the
|
---|
190 | user if a rename results in replacement of an already-existing filename.
|
---|
191 | If -q is given, no notification is issued.
|
---|
192 | -Q: Suppress other warnings. By default, a warning is issued if no files are
|
---|
193 | selected for acting upon. If -Q is given, no warning is issued.
|
---|
194 | -v: Show the rename commands being executed.
|
---|
195 | -t: Show what rename commands would be done, but do not carry them out."
|
---|
196 | exit 0
|
---|
197 | ;;
|
---|
198 | f)
|
---|
199 | check=false
|
---|
200 | ;;
|
---|
201 | q)
|
---|
202 | warn=false
|
---|
203 | ;;
|
---|
204 | Q)
|
---|
205 | warnNoFiles=false
|
---|
206 | ;;
|
---|
207 | r)
|
---|
208 | warnNoFiles=false
|
---|
209 | recurse=true
|
---|
210 | ;;
|
---|
211 | t)
|
---|
212 | tell=true
|
---|
213 | ;;
|
---|
214 | v)
|
---|
215 | verbose=true
|
---|
216 | ;;
|
---|
217 | x)
|
---|
218 | verbose=true
|
---|
219 | debug=true
|
---|
220 | ;;
|
---|
221 | p)
|
---|
222 | inclPats[inclCt]=$OPTARG
|
---|
223 | ((inclCt+=1))
|
---|
224 | ;;
|
---|
225 | P)
|
---|
226 | exclPats[exclCt]=$OPTARG
|
---|
227 | ((exclCt+=1))
|
---|
228 | ;;
|
---|
229 | m)
|
---|
230 | # Store operation for each segment number in ops[num]
|
---|
231 | # Store ending segment number in op_end_seg[num]
|
---|
232 | range=${OPTARG%%=*}
|
---|
233 | op=${OPTARG#*=}
|
---|
234 | start=${range%%:*}
|
---|
235 | end=${range#*:}
|
---|
236 | if [[ "$start" != +([0-9]) || "$start" -eq 0 ]]; then
|
---|
237 | print -ru2 -- "$name: Bad starting segment number given with -m: $start"
|
---|
238 | exit 1
|
---|
239 | fi
|
---|
240 | if [[ "$end" != +([0-9]) || "$end" -eq 0 ]]; then
|
---|
241 | print -ru2 -- "$name: Bad ending segment number given with -m: $end"
|
---|
242 | exit 1
|
---|
243 | fi
|
---|
244 | if [[ start -gt end ]]; then
|
---|
245 | print -ru2 -- "$name: Ending segment ($end) is less than starting segment ($start)"
|
---|
246 | exit 1
|
---|
247 | fi
|
---|
248 | if [[ "$op" != @(|*[!_a-zA-Z0-9])i@(|[!_a-zA-Z0-9]*) ]]; then
|
---|
249 | print -ru2 -- \
|
---|
250 | "$name: Operation given with -m does not reference 'i': $op"
|
---|
251 | exit 1
|
---|
252 | fi
|
---|
253 | # Test whether operation is legal. let returns 1 both for error
|
---|
254 | # indication and when last expression evaluates to 0, so evaluate 1
|
---|
255 | # after test expression.
|
---|
256 | i=1
|
---|
257 | let "$op" 1 2>/dev/null || {
|
---|
258 | print -ru2 -- \
|
---|
259 | "$name: Bad operation given with -m: $op"
|
---|
260 | exit 1
|
---|
261 | }
|
---|
262 | ops[start]=$op
|
---|
263 | op_end_seg[start]=$end
|
---|
264 | ;;
|
---|
265 | z)
|
---|
266 | if [[ "$OPTARG" != +([0-9]) || "$OPTARG" -eq 0 ]]; then
|
---|
267 | print -ru2 -- "$name: Bad length given with -z: $OPTARG"
|
---|
268 | exit 1
|
---|
269 | fi
|
---|
270 | typeset -Z$OPTARG j || exit 1
|
---|
271 | ;;
|
---|
272 | +?) # no way to tell getopts to not treat +x as an option
|
---|
273 | print -r -u2 "$name: Do not prefix options with '+'."
|
---|
274 | exit 1
|
---|
275 | ;;
|
---|
276 | :)
|
---|
277 | print -r -u2 \
|
---|
278 | "$name: Option -$OPTARG requires a value.
|
---|
279 | $Usage
|
---|
280 | Use -h for help."
|
---|
281 | exit 1
|
---|
282 | ;;
|
---|
283 | \?)
|
---|
284 | print -r -u2 \
|
---|
285 | "$name: -$OPTARG: no such option.
|
---|
286 | $Usage
|
---|
287 | Use -h for help."
|
---|
288 | exit 1
|
---|
289 | ;;
|
---|
290 | esac
|
---|
291 | done
|
---|
292 |
|
---|
293 | # remove args that were options
|
---|
294 | let OPTIND=OPTIND-1
|
---|
295 | shift $OPTIND
|
---|
296 |
|
---|
297 | oldpat=$1
|
---|
298 | newpat=$2
|
---|
299 |
|
---|
300 | # If -m is given, a non-existant or null newpat should be set to oldpat
|
---|
301 | if [ ${#ops[*]} -gt 0 ]; then
|
---|
302 | case $# in
|
---|
303 | 0)
|
---|
304 | ;;
|
---|
305 | 1)
|
---|
306 | set -- "$oldpat" "$oldpat"
|
---|
307 | newpat=$oldpat
|
---|
308 | $debug && print -ru2 -- "Set new pattern to: $newpat"
|
---|
309 | ;;
|
---|
310 | *)
|
---|
311 | if [ -z "$newpat" ]; then
|
---|
312 | shift 2
|
---|
313 | set -- "$oldpat" "$oldpat" "$@"
|
---|
314 | newpat=$oldpat
|
---|
315 | $debug && print -ru2 -- "Set new pattern to: $newpat"
|
---|
316 | fi
|
---|
317 | ;;
|
---|
318 | esac
|
---|
319 | fi
|
---|
320 |
|
---|
321 | # Make sure input patterns that contain whitespace can be expanded properly
|
---|
322 | IFS=
|
---|
323 |
|
---|
324 | origPat=$oldpat
|
---|
325 |
|
---|
326 | # Generate list of filenames to act on.
|
---|
327 | case $# in
|
---|
328 | [01])
|
---|
329 | print -u2 "$Usage\nUse -h for help."
|
---|
330 | exit 1
|
---|
331 | ;;
|
---|
332 | 2)
|
---|
333 | if $recurse; then
|
---|
334 | print -r -u2 "$name: No directory names given with -r. Use -h for help."
|
---|
335 | exit 1
|
---|
336 | fi
|
---|
337 | set -- $oldpat # Get list of all filenames that match 1st globbing pattern.
|
---|
338 | if [[ ! -a $1 ]]; then
|
---|
339 | $warnNoFiles && print -r -- "$name: No filenames match this pattern: $oldpat"
|
---|
340 | exit
|
---|
341 | fi
|
---|
342 | ;;
|
---|
343 | *)
|
---|
344 | shift 2
|
---|
345 | ;;
|
---|
346 | esac
|
---|
347 |
|
---|
348 | integer patSegNum=1 numPatSegs
|
---|
349 |
|
---|
350 | # For old ksh
|
---|
351 | # while [[ "$oldpat" = *'[\*\?]'* ]]; do
|
---|
352 |
|
---|
353 | # Example oldpat: foo*.a
|
---|
354 | # Example newpat: bar*.b
|
---|
355 |
|
---|
356 | # Build list of non-pattern segments and globbing segments found in arguments.
|
---|
357 | # Note the patterns given are used to get the list of filenames to act on,
|
---|
358 | # to delimit constant segments, and to determine which parts of filenames are
|
---|
359 | # to be replaced.
|
---|
360 | # Examples given for first iteration (in the example, the only iteration)
|
---|
361 | # The || newpat is to ensure that new pattern does not have more globbing
|
---|
362 | # segments than old pattern
|
---|
363 | while [[ "$oldpat" = *@([\*\?]|\[+([!\]])\])* ||
|
---|
364 | "$newpat" = *@([\*\?]|\[+([!\]])\])* ]]; do
|
---|
365 | ## Get leftmost globbing pattern in oldpat
|
---|
366 |
|
---|
367 | # Make r be oldpat with smallest left piece that includes a globbing
|
---|
368 | # pattern removed from it
|
---|
369 | r=${oldpat#*@([\*\?]|\[+([!\]])\])} # r=.a
|
---|
370 | # Make pat be oldpat with the above removed from it, leaving smallest
|
---|
371 | # left piece that includes a globbing pattern
|
---|
372 | pat=${oldpat%%"$r"} # pat=foo*
|
---|
373 | # Make l be pat with the globbing pattern removed from the right,
|
---|
374 | # leaving a constant string
|
---|
375 | l=${pat%@([\*\?]|\[+([!\]])\])} # l=foo
|
---|
376 | # Remove the constant part of pat from the left, leaving the globbing
|
---|
377 | # pattern
|
---|
378 | pat=${pat#"$l"} # pat=*
|
---|
379 |
|
---|
380 | # Do the same thing for newpat, solely to provide a reliable test that
|
---|
381 | # both oldpat & newpat contain exactly the same sequence of globbing
|
---|
382 | # patterns.
|
---|
383 | r=${newpat#*@([\*\?]|\[+([!\]])\])} # r=.b
|
---|
384 | npat=${newpat%%"$r"} # pat=bar*
|
---|
385 | l=${npat%@([\*\?]|\[+([!\]])\])} # l=bar
|
---|
386 | npat=${npat#"$l"} # npat=*
|
---|
387 |
|
---|
388 | if [[ "$pat" != "$npat" ]]; then
|
---|
389 | print -ru2 -- \
|
---|
390 | "$name: Old-pattern and new-pattern do not have the same sequence of globbing chars.
|
---|
391 | Pattern segment $patSegNum: Old pattern: $pat New pattern: $npat"
|
---|
392 | exit 1
|
---|
393 | fi
|
---|
394 |
|
---|
395 | ## Find parts before & after pattern
|
---|
396 | # oldpre[] stores the old constant part before the pattern,
|
---|
397 | # so that it can be removed and replaced with the new constant part.
|
---|
398 | oldpre[patSegNum]=${oldpat%%"$pat"*} # oldpre[1]=foo
|
---|
399 | # oldsuf stores the part that follows the globbing pattern,
|
---|
400 | # so that it too can be removed.
|
---|
401 | # After oldpre[] & oldsuf[] have been removed from a filename, what remains
|
---|
402 | # is the part matched by the globbing pattern, which is to be retained.
|
---|
403 | oldsuf[patSegNum]=${oldpat#*"$pat"} # oldsuf[1]=.a
|
---|
404 | # newpre[] stores the new constant part before the pattern,
|
---|
405 | # so that it can be used to replace the old constant part.
|
---|
406 | newpre[patSegNum]=${newpat%%"$pat"*} # newpre[1]=bar
|
---|
407 | # Get rid of processed part of patterns
|
---|
408 | oldpat=${oldpat#${oldpre[patSegNum]}"$pat"} # oldpat=.a
|
---|
409 | newpat=${newpat#${newpre[patSegNum]}"$pat"} # newpat=.b
|
---|
410 | # Store either * or ? in pats[], depending on whether this segment matches 1
|
---|
411 | # or any number of characters.
|
---|
412 | [[ "$pat" = \[* ]] && pat=?
|
---|
413 | pats[patSegNum]=$pat
|
---|
414 | ((patSegNum+=1))
|
---|
415 | done
|
---|
416 |
|
---|
417 | if [ patSegNum -eq 1 ]; then
|
---|
418 | print -u2 "No globbing chars in pattern."
|
---|
419 | exit 1
|
---|
420 | fi
|
---|
421 |
|
---|
422 | oldpre[patSegNum]=${oldpat%%"$pat"*} # oldpre[2]=.a
|
---|
423 | oldsuf[patSegNum]=${oldpat#*"$pat"} # oldsuf[2]=.a
|
---|
424 | newpre[patSegNum]=${newpat%%"$pat"*} # newpre[2]=.b
|
---|
425 |
|
---|
426 | numPatSegs=patSegNum
|
---|
427 |
|
---|
428 | if $debug; then
|
---|
429 | patSegNum=1
|
---|
430 | while [[ patSegNum -le numPatSegs ]]; do
|
---|
431 | print -ru2 -- \
|
---|
432 | "Old prefix: <${oldpre[patSegNum]}> Old suffix: <${oldsuf[patSegNum]}> New prefix: <${newpre[patSegNum]}> Pattern: <${pats[patSegNum]}>"
|
---|
433 | ((patSegNum+=1))
|
---|
434 | done
|
---|
435 | fi
|
---|
436 |
|
---|
437 | # Example filename: foox.a
|
---|
438 | # Example oldpat: foo*.a
|
---|
439 | # Example newpat: bar*.b
|
---|
440 |
|
---|
441 | integer numFiles=0
|
---|
442 |
|
---|
443 | # Usage: renameFile filename [dirname]
|
---|
444 | # [dirname] is a directory name to prefix filenames with when they are printed
|
---|
445 | # for informational purposes.
|
---|
446 | # Uses globals:
|
---|
447 | # inclCt exclCt inclPats[] exclPats[] ops[]
|
---|
448 | # numPatSegs oldpre[] oldsuf[] newpre[] pats[]
|
---|
449 | # check warn tell verbose name
|
---|
450 | # Modifies globals: numFiles
|
---|
451 | function renameFile {
|
---|
452 | typeset file=$1 subdir=$2
|
---|
453 | integer patSegNum patnum
|
---|
454 | typeset origname porigname newfile matchtext pnewfile matchsegs
|
---|
455 | integer startseg endseg
|
---|
456 |
|
---|
457 | origname=$file # origname=foox.a
|
---|
458 | porigname=$subdir$file
|
---|
459 | # Unfortunately, ksh88 does not do a good job of allowing for patterns
|
---|
460 | # stored in variables. Without the conditional expression being eval'ed,
|
---|
461 | # only sh patterns are recognized. If the expression is eval'ed, full
|
---|
462 | # ksh expressions can be used, but then expressions that contain whitespace
|
---|
463 | # break unless the user passed a pattern with the whitespace properly
|
---|
464 | # quoted, which is not intuititive. This is fixed in ksh93; full patterns
|
---|
465 | # work without being eval'ed.
|
---|
466 | if [ inclCt -gt 0 ]; then
|
---|
467 | patnum=0
|
---|
468 | while [ patnum -lt inclCt ]; do
|
---|
469 | [[ "$file" = ${inclPats[patnum]} ]] && break
|
---|
470 | ((patnum+=1))
|
---|
471 | done
|
---|
472 | if [ patnum -eq inclCt ]; then
|
---|
473 | $debug && print -ru2 -- "Skipping not-included filename '$porigname'"
|
---|
474 | return 1
|
---|
475 | fi
|
---|
476 | fi
|
---|
477 | patnum=0
|
---|
478 | while [ patnum -lt exclCt ]; do
|
---|
479 | if [[ "$file" = ${exclPats[patnum]} ]]; then
|
---|
480 | $debug && print -ru2 -- "Skipping excluded filename '$porigname'"
|
---|
481 | return 1
|
---|
482 | fi
|
---|
483 | ((patnum+=1))
|
---|
484 | done
|
---|
485 | # Extract matching segments from filename
|
---|
486 | ((numFiles+=1))
|
---|
487 | patSegNum=1
|
---|
488 | while [[ patSegNum -le numPatSegs ]]; do
|
---|
489 | # Remove a fixed prefix iteration: 1 2
|
---|
490 | file=${file#${oldpre[patSegNum]}} # file=x.a file=
|
---|
491 | # Save the part of this suffix that is to be retained. To do this, we
|
---|
492 | # need to know what part of the suffix matched the current globbing
|
---|
493 | # segment. If the globbing segment is a *, this is done by removing
|
---|
494 | # the minimum part of the suffix that matches oldsuf (since * matches
|
---|
495 | # the longest segment possible). If the globbing segment is ? or []
|
---|
496 | # (the latter has already been coverted to ?), it is done by taking the
|
---|
497 | # next character.
|
---|
498 | if [ "${pats[patSegNum]}" == \? ]; then
|
---|
499 | matchtext=${file#?}
|
---|
500 | matchtext=${file%$matchtext}
|
---|
501 | else
|
---|
502 | matchtext=${file%${oldsuf[patSegNum]}} # matchtext=x matchtext=
|
---|
503 | fi
|
---|
504 | $debug && print -ru2 -- "Matching segment $patSegNum: $matchtext"
|
---|
505 | file=${file#$matchtext} # file=.a file=.a
|
---|
506 |
|
---|
507 | matchsegs[patSegNum]=$matchtext
|
---|
508 | ((patSegNum+=1))
|
---|
509 | done
|
---|
510 |
|
---|
511 | # Paste fixed and matching segments together to form new filename.
|
---|
512 | patSegNum=0
|
---|
513 | newfile=
|
---|
514 | while [[ patSegNum -le numPatSegs ]]; do
|
---|
515 | matchtext=${matchsegs[patSegNum]}
|
---|
516 | startseg=patSegNum
|
---|
517 | if [ -n "${ops[startseg]}" ]; then
|
---|
518 | endseg=${op_end_seg[startseg]}
|
---|
519 | while [ patSegNum -lt endseg ]; do
|
---|
520 | ((patSegNum+=1))
|
---|
521 | matchtext=$matchtext${matchsegs[patSegNum]}
|
---|
522 | done
|
---|
523 | if [[ "$matchtext" != +([-0-9]) ]]; then
|
---|
524 | print -ru2 -- \
|
---|
525 | "Segment(s) $startseg - $endseg ($matchtext) of file '$porigname' do not form an integer; skipping this file."
|
---|
526 | return 2
|
---|
527 | fi
|
---|
528 | i=$matchtext
|
---|
529 | let "j=${ops[startseg]}" || {
|
---|
530 | print -ru2 -- \
|
---|
531 | "Operation failed on segment(s) $startseg - $endseg ($matchtext) of file '$file'; skipping this file."
|
---|
532 | return 2
|
---|
533 | }
|
---|
534 | $debug && print -ru2 -- "Converted $matchtext to $j"
|
---|
535 | matchtext=$j
|
---|
536 | fi
|
---|
537 | newfile=$newfile${newpre[startseg]}$matchtext # newfile=barx newfile=barx.b
|
---|
538 | ((patSegNum+=1))
|
---|
539 | done
|
---|
540 |
|
---|
541 | pnewfile=$subdir$newfile
|
---|
542 | if $check && [ -e "$newfile" ]; then
|
---|
543 | $warn &&
|
---|
544 | print -ru2 -- "$name: Not renaming \"$porigname\"; destination filename \"$pnewfile\" already exists."
|
---|
545 | return 2
|
---|
546 | fi
|
---|
547 | if $tell; then
|
---|
548 | print -n -r -- "Would move: $porigname -> $pnewfile"
|
---|
549 | $warn && [ -e "$newfile" ] && print -n -r " (destination filename already exists; would replace it)"
|
---|
550 | print ""
|
---|
551 | else
|
---|
552 | if $verbose; then
|
---|
553 | print -n -r -- "Moving: $porigname -> $pnewfile"
|
---|
554 | $warn && [ -e "$newfile" ] && print -n -r -- " (replacing old destination filename \"$pnewfile\")"
|
---|
555 | print ""
|
---|
556 | elif $warn && [ -e "$newfile" ]; then
|
---|
557 | print -r -- "$name: Note: Replacing old file \"$pnewfile\""
|
---|
558 | fi
|
---|
559 | mv -f -- "$origname" "$newfile"
|
---|
560 | fi
|
---|
561 | }
|
---|
562 |
|
---|
563 | if $recurse; then
|
---|
564 | oPWD=$PWD
|
---|
565 | find "$@" -depth -type d ! -name '*
|
---|
566 | *' -print | while read dir; do
|
---|
567 | cd -- "$oPWD"
|
---|
568 | if cd -- "$dir"; then
|
---|
569 | for file in $origPat; do
|
---|
570 | renameFile "$file" "$dir/"
|
---|
571 | done
|
---|
572 | else
|
---|
573 | print -ru2 -- "$name: Could not access directory '$dir' - skipped."
|
---|
574 | fi
|
---|
575 | done
|
---|
576 | else
|
---|
577 | for file; do
|
---|
578 | renameFile "$file"
|
---|
579 | done
|
---|
580 | fi
|
---|
581 |
|
---|
582 | if [ numFiles -eq 0 ]; then
|
---|
583 | $warnNoFiles && print -ru2 -- \
|
---|
584 | "$name: All filenames were excluded by patterns given with -p or -P."
|
---|
585 | fi
|
---|