[comp.unix.questions] Need a file renaming facility

inc@tc.fluke.COM (Gary Benson) (04/27/88)

Hello,

I recently subscribed to this group to see if it's an appropriate place to
ask a question I've had for some time now. It seems to be, so here goes:

I work in a publications department, and in the nature of our business, we
create, rename, and destroy a LOT of files. A typical directory may have
many similar file names. Wildcards help in deleting unneeded files, but not
in copying or moving them. For example, consider the following files in a
directory associated with a technical manual:

    QT.1.r	   QT.4.r	  QT.A.r
    QT.2.r	   QT.5.r	  QT.annotations.r
    QT.3.r	   QT.6.r	  QT.toc.r

For information only, the product is called QuickTools, there is one file
per section of the manual (1 through 6), one for Appendix A, one for
illustration annotations, and the table of contents. In preparing this
manual for production, I run a shell script called "prep" that gets rid of
extra spacing, intermediate formatting commands, comments and so on, and
creates an output file for each input file. Now my directory now looks like
this:

    QT.1.r	   QT.4.r	       QT.A.r
    Qt.1.r.pre	   QT.4.r.pre	       QT.A.r.pre
    QT.2.r	   QT.5.r	       QT.annotations.r
    QT.2.r.pre	   QT.5.r.pre	       QT.annotations.r.pre
    QT.3.r	   QT.6.r	       QT.toc.r
    QT.3.r.pre	   QT.6.r.pre	       QT.toc.r.pre

Now I want to rename all those ".pre" files to the same name without ".pre".
In other words, I want the directory to look like it did when I began, (like
separate mv commands, or cp followed by rm *.pre) separate mv and I realize
that a smarter shell script could have taken care of this as part of the
prepping process, but there are many ways that people in our group wind up
with associated files with similar filenames they want to rename all at
once. The alternative (separate 'mv' commands for each section) is
time-consuming and error prone. What I'm looking for is a general-purpose
renaming facility that (I hope) takes wild cards, with this kind of synatax:


    rename -f QT.?.r.pre QT.?.r

	      -or-

    rename QT.*.r.pre QT.*.r

	   -or even-

    rename QT.?.r.pre \#.r (to change QT.1.r.pre to 1.r, etc)

Hopefully a -f option would do it without question, -i would ask before
blowing away the file of the same name and -i being the default. No -r
option!

Has anyone invented such a thing? If so, could you send me a copy? If not,
can anyone explain a workable approach to doing this?

I'd appreciate any help you can give me.

___
Gary Benson		   -_-_-_-_-_-_-_-_-inc@tc.fluke.com_-_-_-_-_-_-_-_-_-_
Publication Services	   Ensign Benson, Space Cadet, Digital Circus, Sector R
John Fluke Mfg. Co. Inc.   _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-




-- 
Gary Benson   
Publication Services	-=[ inc : 5367 : 232E ]=-

bowles@lll-crg.llnl.gov (Jeff Bowles) (04/27/88)

Hmm. Hard to do directly, without hacking the shell's view of
metacharacters. Perhaps you want something like this:

rename '\(.*\).f' *.f

in which the expression is something normally given to expr(1).
Such a script would follow this form:
	#!/bin/sh
	
	ex=$1
	shift
	for f in $*
	do
		newf=`expr $f : "$ex"`
		mv $f $newf
	done
Of course you'd embellish this, make it possible to pass flags to the
mv(1) command, and provide for files with "#" in the name, but I submit
that to ignore the existing tools would not only waste your time, but
run the risk of making a new tool that doesn't fit in well with the existing
ones.

[ "Isn't it neat when you can spell your name with the different flags you've
added to a command?" -  overheard at a Usenix a while back.]

	Jeff Bowles

gwyn@brl-smoke.ARPA (Doug Gwyn ) (04/27/88)

In article <3564@fluke.COM> inc@tc.fluke.COM (Gary Benson) writes:
>Now I want to rename all those ".pre" files to the same name without ".pre".

What I usually do in such circumstances is:
for i in *.pre; do mv $i `basename $i .pre`; done

davidsen@steinmetz.ge.com (William E. Davidsen Jr) (04/28/88)

  As long as the rename is the way you describe, "rename a file with a
suffix to be the same name without the siffix" the solution is easy. It
used sh (or ksh) as written:
	for src in *.suffix		# all names of this type
	do
	 dest=`basename $src .suffix`	# strip the suffix
	 mv $src $dest			# and move the file
	done

  I don't have access to a pure BSD system to see if basename is there;
it's in Ultrix and SunOS which are as close as I come.
-- 
	bill davidsen		(wedu@ge-crd.arpa)
  {uunet | philabs | seismo}!steinmetz!crdos1!davidsen
"Stupidity, like virtue, is its own reward" -me

decot@hpcupt1.HP.COM (Dave Decot) (04/28/88)

To move *.r.pre to *.r, do this:

	% sh
	$ for i in *.pre
	> do
	>     name=`echo $i | sed 's/.pre$//'`
	>     mv $i $name
	> done
	$ exit
        % 

Dave Decot
hpda!decot

hunt@csli.STANFORD.EDU (Brian Hunt) (04/28/88)

In article <3564@fluke.COM> inc@tc.fluke.COM (Gary Benson) writes:
>What I'm looking for is a general-purpose
>renaming facility that (I hope) takes wild cards, with this kind of synatax:
>
>    rename -f QT.?.r.pre QT.?.r
>
>	      -or-
>
>    rename QT.*.r.pre QT.*.r

This may be overkill, but here's a shell script I've been using for
some time to do often complicated renaming of multiple files.  It's
probably rather clumsy, but has worked quite well for me.  I'm sure
there are many possibilties I haven't tried, though, so USE AT YOUR
OWN RISK.  I just added the part that passes flags to mv(1), so that
bit is relatively untested.  I'm sure it has a relatively limited set
of characters it can handle in a filename; one possibly unexpected
character it will choke on is ':', since that's what the script uses
as the delimiter for the sed(1) substitution function.  Beats choking
on '/', at least...

The syntax is as follows:

rename [-flags] "exp1" "exp2"

where exp1 describes the files to be renamed using the wildcards '?'
and '*', and exp2 gives the pattern for the new names of the files
using the same sequence of wildcards as exp1.  For example,

rename -i "a*b?*c" "x*?y*z"

would use "mv -i" to move files a12b345c and a1b2c to x123y45z and
x12yz, respectively, while leaving the file a12bc unchanged.  That is,
each wildcard in exp2 is replaced by the string which was matched by
the corresponding wildcard in exp1.  In addition, if a wildcard in
exp2 is preceded by a tilde, the corresponding string is dropped from
the destination filename (possibly making it non-unique.)  So,

rename "*.*" "*~*"

would move files a.b, a.c, and b.c to a, a, and b respectively, thus
losing the contents of a.b.  Probably best to always use -i option
when using this feature...

Note that the arguments to rename must be quoted somehow in order for
the wildcards not to be globbed away.  To save me from worrying about
this, I have the following in my .cshrc:

alias ren 'rename -i "\!:1" "\!:2"'

At most nine wildcards are allowed.  All filename changes are echoed
on the standard output.

Finally, here is the script.  Feel free to use, copy, and modify it
as you please, with or without attribution; i.e., it's all yours...

Brian Hunt
Dept. of Mathematics
Stanford University

#!/bin/csh -f
set noglob
set flags
while (`echo "$1" | cut -c1` == "-")
	set flags=($flags $1)
	shift
end
if (`echo "$1" | sed 's:[^?*]::g'` != `echo "$2" | sed 's:[^?*]::g'`) then
	echo "rename:  Please use same wildcards in both arguments."
	exit 1
endif
unset noglob
set files=$1
set noglob
set oldexp=`echo "$1" | sed -e 's:?:\\(.\\):g' -e 's:\*:\\(.*\\):g'`
set newexp="$2"
foreach digit (1 2 3 4 5 6 7 8 9)
	set tmpexp=`echo "$newexp" | sed 's:[?*]:\\'"$digit":`
	if ("$tmpexp" == "$newexp") then
		break
	else
		set newexp="$tmpexp"
	endif
end
if (`echo "$newexp" | grep -c '[?*]'` > 0) then
	echo "rename:  Too many wildcards."
	exit 2
endif
set newexp=`echo "$tmpexp" | sed 's:~\\[1-9]::g'`
foreach oldfile ($files)
	set newfile=`echo "$oldfile" | sed s:^"$oldexp"\$:"$newexp":`
	echo "moving $oldfile to $newfile"
	mv $flags "$oldfile" "$newfile"
end
exit 0

rbj@icst-cmr.arpa (Root Boy Jim) (05/04/88)

   From: Gary Benson <inc@tc.fluke.COM>

   Hello,

Good morning, Mister Benson, I see you're doing well...

   Now I want to rename all those ".pre" files to the same name without ".pre".

As usual, Doug Gwyn posted the quickest, most succinct, and most universal
solution (I do say something nice about him every once in awhile :-). Some
of the other solutions that were posted were incredibly unwieldy, and their
posters should rethink their approach to the problem. Repent heathens!

However, for those of us with the csh, there is yet another solution:

	foreach x (*.pre)
		mv $x $x:r
	end

This may be typed directly into the shell, but only works with suffixes
delimited with dots. Multiple dots work, so `set x=a.b.c; echo $x:r'
yields `a.b'. You may add the -f or -i flags directly to mv if desired.

   Gary Benson		   -_-_-_-_-_-_-_-_-inc@tc.fluke.com_-_-_-_-_-_-_-_
   Publication Services	   Ensign Benson, Space Cadet, Digital Circus, Sector R
   John Fluke Mfg. Co. Inc.   _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-

	(Root Boy) Jim Cottrell	<rbj@icst-cmr.arpa>
	National Bureau of Standards
	Flamer's Hotline: (301) 975-5688
	The opinions expressed are solely my own
	and do not reflect NBS policy or agreement
	Sign my PETITION.

ctr@Stride.COM (Chris Reimer) (05/05/88)

In article <3564@fluke.COM> inc@tc.fluke.COM (Gary Benson) writes:
>
>    QT.1.r	   QT.4.r	       QT.A.r
>    Qt.1.r.pre	   QT.4.r.pre	       QT.A.r.pre
>
>Now I want to rename all those ".pre" files to the same name without ".pre".

Try:
	foo% foreach i ( `ls *.pre | sed 's/.pre$//'` )
	? echo "Moving ${i}..."
	? mv ${i}.pre $i
	? end

Obviously (I hope), this must be run under csh.  Enjoy!

--- 
Christian Reimer	"Through Truth and Justice, Virtue will Grow!"
ctr@xpiinc.uu.net			- The Salamander
...!uunet!xpiinc!ctr
---

amoss%HUJINIX.BITNET@cunyvm.cuny.edu (Amos Shapira) (05/09/88)

Chris Reimer (ctr@stride.com) writes:
>In article <3564@fluke.COM> inc@tc.fluke.COM (Gary Benson) writes:
>>
>>    QT.1.r       QT.4.r           QT.A.r
>>    Qt.1.r.pre       QT.4.r.pre           QT.A.r.pre
>>
>>Now I want to rename all those ".pre" files to the same name without ".pre".
>
>Try:
>    foo% foreach i ( `ls *.pre  sed 's/.pre$//'` )
>    ? echo "Moving ${i}..."
>    ? mv ${i}.pre $i
>    ? end
>
>Obviously (I hope), this must be run under csh.  Enjoy!

      Could make it simpler, and much more importnt, faster.
    My suggestion is to use basename(1), like this:

    % foreach i (*.pre)
    ? echo Moving $i...
    ? mv $i `basename $i .pre`
    ? end

      basename(1) is under /usr/bin, so I'm not sure if it
    comes with SV systems. But you can write one for yourself.

      Maybe the forking of a new basename program for each
    file is slower than running one, a bit bigger, sed, but
    after the first time the basename program is loaded, it
    stays in memory, and so the rest should take much faster.
    Also basename is much simpler than sed.

      If you still want to use Chris's idea, be carefull to
    use /bin/ls, to prevent aliasing (here at HU, people like
    to alias ls to use the -F flag, which may break Chris's
    solution).

Have fun!
--Amos Shapira
The Hebrew University of Jerusalem, Israel.

==============================================================================
BITNET: amoss@hujinix.bitnet         CSnet: amoss%shum.huji.ac.il@relay.cs.net
UUCPnet: ucbvax!shum.huji.ac.il!amoss      Domain-Style: amoss@shum.huji.ac.il
==============================================================================

"Super users do it without asking for permission." - me

davy@ea.ecn.purdue.edu (Dave Curry) (05/09/88)

In article <13840@brl-adm.ARPA> amoss%HUJINIX.BITNET@cunyvm.cuny.edu (Amos Shapira) writes:
>Chris Reimer (ctr@stride.com) writes:
>>
>>Try:
>>    foo% foreach i ( `ls *.pre  sed 's/.pre$//'` )
>>    ? echo "Moving ${i}..."
>>    ? mv ${i}.pre $i
>>    ? end
>>
>>Obviously (I hope), this must be run under csh.  Enjoy!
>
>      Could make it simpler, and much more importnt, faster.
>    My suggestion is to use basename(1), like this:
>
>    % foreach i (*.pre)
>    ? echo Moving $i...
>    ? mv $i `basename $i .pre`
>    ? end
>
>      basename(1) is under /usr/bin, so I'm not sure if it
>    comes with SV systems. But you can write one for yourself.

If you're going for speed, then forking and execing basename for every
file name is certainly not the solution.  I suspect for any number of
files greater than 3 or 4, the ls/sed answer is faster.

But, both of you are doing things the wrong way.  If you're going to
use "csh", then instead of simply writing an "sh" script in "csh"
syntax, you might as well use some of "csh"'s useful features, namely
the colon modifiers (or whatever they're called):

	% foreach i (*.pre)
	? echo Moving $i...
	? mv $i $i:r
	? end

The ":r" operator removes the first (working from the right)
".anything" from a file name.  It's exactly equivalent to your
basename version above, only about a zillion times faster.  There are
other colon modifiers which let you take the basename of a file (:t),
the directory part of a file name (:h), and the extension (.anything)
part of a file name (:e).  And yes, it's all documented in the manual
page, this isn't secret magic stuff.


--Dave Curry

rrr@naucse.UUCP (Bob Rose ) (05/10/88)

Amos Shapira writes:
> Chris Reimer writes:
> >Gary Benson writes:
> >>    QT.1.r       QT.4.r           QT.A.r
> >>    Qt.1.r.pre       QT.4.r.pre           QT.A.r.pre
> >>Now I want to rename all those ".pre" files to the same name without ".pre".
> >    foo% foreach i ( `ls *.pre  sed 's/.pre$//'` )
> >    ? echo "Moving ${i}..."
> >    ? mv ${i}.pre $i
> >    ? end
> 
>       Could make it simpler, and much more importnt, faster.
>     % foreach i (*.pre)
>     ? echo Moving $i...
>     ? mv $i `basename $i .pre`
>     ? end

So we want to make this one time command run faster, then why
are we running `basename' for each arguement. Lets use the real
power of the csh.

	% foreach i (*.pre)
	? echo Moving $i...
	? mv $i $i:r
	? end

This may also be to slow also. Has anybody modified `mv' to move files
to their basename. Something like

	% mv -b *.pre

:^) 8^) :`) O^) ;^) |^) :~) :^} :^] &^) %^) :^{) X^) (Hopefully thats enough)

jcl@bdrc.COM (John C. Lusth) (05/10/88)

>In article <3564@fluke.COM> inc@tc.fluke.COM (Gary Benson) writes:
>
>    QT.1.r	   QT.4.r	       QT.A.r
>    Qt.1.r.pre	   QT.4.r.pre	       QT.A.r.pre
>
>Now I want to rename all those ".pre" files to the same name without ".pre".


Here is a shell script that renames portions of filenames:

#!/bin/csh #########################################################
#
#   mv+ pattern1 pattern2 file1 [ file2 ...  ]
#
#   rename files by replacing the last pattern1 with pattern2

set x = $1	# what it was
shift
set y = $1	# what it shall be
shift

foreach f ( $* )
    if ( `expr match $f '.*'$x'.*'` ) then      
	set a = `expr match $f '\(.*\)'$x'.*'`
	set b = `expr match $f '.*'$x'\(.*\)'`
	echo mv $f $a$y$b
	mv $f $a$y$b
    else
	echo $f not moved.
    endif
end
echo finished.

####################################################################

If you were to name this script mv+ ( I for the life of me can't think
up a good name for it),  the following command would accomplish your task

    mv+ .pre "" *

Every once in a while, this shell script can be quite useful.  You can
also do things like

    mv+ "[abc]" z *

which replaces the last letter a, b, or, c with z in any file names containing
an a, b, or, c.  Messing around with regular expressions can be hazardous
though.  In general, one should use literals for pattern1.

John C. Lusth
Becton Dickinson Research Center
Research Triangle Park, NC 

...!mcnc!bdrc!jcl

jcl@bdrc.COM (John C. Lusth) (05/11/88)

In article <324@bdrc.UUCP> jcl@bdrc.UUCP (John C. Lusth) writes:
<Here is a shell script that renames portions of filenames:
<
<#!/bin/csh #########################################################
<#
<#   mv+ pattern1 pattern2 file1 [ file2 ...  ]
<#
<#   rename files by replacing the last pattern1 with pattern2
<
	[shell script and text deleted ]
<
<Every once in a while, this shell script can be quite useful.  You can
<also do things like
<
<    mv+ "[abc]" z *
<
<which replaces the last letter a, b, or, c with z in any file names containing
<an a, b, or, c.  Messing around with regular expressions can be hazardous
<though.  In general, one should use literals for pattern1.

Oops.  In order to use regular expressions for pattern1, insert the line

    set noglob

in the beginning of the script.  Sorry about that.

John C. Lusth
Becton Dickinson Research Center
Research Triangle Park, NC 

...!mcnc!bdrc!jcl

cudcv@daisy.warwick.ac.uk (Rob McMahon) (05/11/88)

In article <784@stride.Stride.COM> ctr@xpiinc.uu.net (Christian Reimer) writes:
>In article <3564@fluke.COM> inc@tc.fluke.COM (Gary Benson) writes:
>>Now I want to rename all those ".pre" files to the same name without ".pre".
>
>Try:
>	foo% foreach i ( `ls *.pre | sed 's/.pre$//'` )
>	? echo "Moving ${i}..."
>	? mv ${i}.pre $i
>	? end

Or even

	foo% foreach i ( *.pre )
	? echo "Moving ${i}..."
	? mv $i $i:r
	? end

Rob
-- 
UUCP:   ...!mcvax!ukc!warwick!cudcv	PHONE:  +44 203 523037
JANET:  cudcv@uk.ac.warwick.cu          ARPA:   cudcv@cu.warwick.ac.uk
Rob McMahon, Computing Services, Warwick University, Coventry CV4 7AL, England

leo@philmds.UUCP (Leo de Wit) (05/20/88)

In article <2956@ea.ecn.purdue.edu> davy@ea.ecn.purdue.edu.UUCP (Dave Curry) writes:
> ...[stuff deleted]...
>>>Try:
>>>    foo% foreach i ( `ls *.pre  sed 's/.pre$//'` )
>>>    ? echo "Moving ${i}..."
>>>    ? mv ${i}.pre $i
>>>    ? end
> ...[stuff deleted about someone using basename each time in the loop]...
>
>If you're going for speed, then forking and execing basename for every
>file name is certainly not the solution.  I suspect for any number of
>files greater than 3 or 4, the ls/sed answer is faster.
> ...[further discussion using csh features]...

I agree about not using basename inside the loop (besides, sed is not that
big and you use it only once), but still like the sed solution more;
it is more flexible and more 'Un*x-style': let each tool do it's own dedicated
job. Sed is good at conversions. I used a similar construct to rename
VMS (have to clean my mouth now 8-) filenames to Un*x filenames, i.e.
lowercase conversion, version stripping. Sed can do just that, e.g.

------------- Start Here -------------
#!/bin/sh

set `ls *\;* 2>/dev/null |sed '
p
s/\([^ 	]*\);[0-9]*/\1/
y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'`

# The positional parameters now are: oldname1 newname1 oldname2 newname2 etc.

while :
do
	case $# in
	0) exit 0;;
	esac
	mv $1 $2
	shift; shift
done
------------- End Here -------------

and you can do all other kinds of conversions, too.

        Leo.