Articles January 14, 2007 at 10:49 am

Filename Renaming

There’s been a fair amount of discussion recently regarding filename renaming, and while this is indeed one of those things that there are as many ways to do as there are to skin a cat, here’s an example of one way, with a bit of explanation as to how you can customize to suit your own needs.So, here was my exact situation, I had multiple folders with 1000s of files named in a similar, but not identical manner that I needed to rename. The Files in question looked like this “1166139013.000207.mbox:2,” where the first string of numbers was always the same number of characters, but were varied numbers, the second set of numbers were unique, but signified the numerical designation of the file, and the rest of it I really didn’t need. In the long run I wanted “1166139013.000207.mbox:2,” to look like “207.”.

As I needed this to run recursively from a root that I designated, I knew I was going to have to use “find”, and indeed finding the files themselves was the easy part, indeed even stripping the filename down to where I needed it too was relatively simple in the grand scheme of things, actually having the renaming action performed, that was the tough part. I was used to using find, and slinging a “-exec” on the end to accomplish tasks in the past – like finding files older than a certain date and removing them:

<code>
find /Volumes/backups/ -ctime +30 -maxdepth 1 -exec rm -rf {} ; 
</code>

One small problem with this when I was using “sed” to massage my filenames and the -exec, or course starting with -e was being interpreted by sed! Time to think again, and that’s where this one-liner came from:

<code>
find /Volumes/Mail/imap/user * | awk '{print("mv "&#36;1" "&#36;1)}' | sed -e 's/&#91;0-9&#93;&#91;0-9&#93;&#91;0-9&#93;&#91;0-9&#93;&#91;0-9&#93;&#91;0-9&#93;&#91;0-9&#93;&#91;0-9&#93;&#91;0-9&#93;&#91;0-9&#93;.0*//2'  
| sed -e 's/mbox:2.//2'  | /bin/sh &amp;&gt; /dev/null
</code>

So, the find is pretty vanilla, awk may be new to some though – officially it’s defined as a “pattern-directed scanning and processing language”… and in English what it gives you is the ability to take the output of any other comand and filter that to your own devices. To show exactly what awk is capable of doing, I could take the output of a “ps” command and only print the PID and actual process being run by me:

<code>
ps -auxww | grep drina | awk '{print &#36;2 , &#36;11}' 
</code>

Where $2 is the PID and $11 is the process. There are endless possibilities here for use in process and resource monitoring.

The next portion of my one-liner is a sed command. sed is described as a stream editor in the man page, but for most it’s usage is as a “find and replace” tool. Once you start using sed however, you’re going to want all find and replace to function in the same manner, with the ability it has to accept regular expressions. At first glance this looks like a bit of a mess, but the regular expression is pretty straightforward in that the “[0-9]” indicates that the character in question could be any numberical character in the 0-9 range. For a full explanation of regular expressions, and how to use them to your power, check out the wikipedia entry on regular expressions.

Finally, while testing this, I’d left the /bin/sh off the end of this one-liner. This enabled me to ensure all the files I wanted to change were in fact going to format correctly. Passing everything previous to /bin/sh is based around the awk command we spoke about earlier, so it’s really sending a mv command to /bin/sh.

Of course there are many other ways this problem could have been approached, which is part of the beauty of any shell scripting – there’s so many right answers. Hopefully this will help with some of the more recent requests for filename renaming, while also helping you to discover a great pair of shell commands in sed and awk.
…..

About

Andrina Kelly is responsible for anything and everything touched by, or connected to, a Mac at Bell Media, Canada's premiere multimedia company. You may recognize her name from the end credits of Canada's evening news broadcast. She has previously spoken at MacSysAdmin, JAMF National Users Conference, Apple's WWDC, Macworld IT conferences, Mac Networkers Retreat, and Canada MacExpo.

No Comments

  • Here’s a script I wrote a couple of weeks ago to find and rename files that
    look like DOS short file names. These seem to occur when files with
    names longer than 31 characters on a Windows server get copied to a
    local drive on a Mac using Services for Macintosh.

    It’s certainly no one-liner, but I like options. I meant to extend it to support
    launch arguments for the find pattern and the sed command, but haven’t
    gotten around to it. I gave up on one-line because I couldn’t figure out how
    to make the find command support regular expressions. I didn’t think to
    use it just to find everything. One-liners are hard to read, anyway.

    #!/bin/sh
    
    PATTERN="w~[0-9]{1,2}."
    REPLACE="sed s/~/_/"
    
    rename () {
    
    	ENTRY=$1
    
    	echo "$ENTRY"|grep "$PATTERN" >/dev/null
    	if [ $? -eq 0 ]
    	then
    		if [ "$VERBOSE" == "YES" ]
    		then
    			echo "     -> $ENTRY matches"
    		fi
    		NEW=`echo $ENTRY|$REPLACE`
    		mv $ENTRY $NEW
    	fi
    }
    
    dir_rename () {
    
    	DIR=$1
    	rename $DIR
    
    	pushd $DIR
    	CONTENTS=`ls`
    
    	for ITEM in $CONTENTS
    	do
    		rename $ITEM
    	done
    
    	popd
    }
    
    recurse () {
    
    	DIR=$1
    	rename $DIR
    
    	pushd $DIR
    	CONTENTS=`ls`;
    
    	for ITEM in $CONTENTS
    	do
    		rename $ITEM
    		if [ -d $ITEM ]
    		then
    			pushd $DIR
    			recurse $ITEM
    			popd
    		fi
    	done
    }
    
    
    # execution begins here
    # options:
    # -R recurse
    # 
    
    ARGS=`getopt vR $*`
    
    if [ $? != 0 ]
    then
    	echo "Usage: $0 [-vR] [file|directory]"
    	echo " -v verbose"
    	echo " -R recursive"
    fi
    set -- $ARGS
    
    for i
    do
    	case "$i"
    	in
    		-v)
    			VERBOSE="YES";
    			shift;;
    		-R)
    			RECURSE="YES";
    			shift;;
    #		default)
    #			set TARGET=$i;
    #			shift;;
    		--) 
    			shift; break;;
    	esac
    done
    
    if [ -z "$1" ]
    then
    	TARGET=`pwd`
    else
    	TARGET=$1
    fi
    
    if [ -d $TARGET ]
    then
    	if [ "$RECURSE" == "YES" ]
    	then
    		recurse $TARGET
    	else
    		rename $TARGET
    	fi
    else
    	rename $TARGET
    fi
    
    exit
    

Leave a reply

You must be logged in to post a comment.