#!/bin/bash # lowercase # - perform various file rename actions on multiple files # - author: Steven A (stevenaaus@yahoo.com) # - tabstops set at 4. Use ":set ts=4" to edit this file in vim. ######### # Usage # ######### function Usage () { case "$Name" in mv_prefix ) ARG2=" PREFIX" ;; mv_suffix ) ARG2=" SUFFIX" ;; mv_sed ) ARG2=" SCRIPT" ;; mv_char* ) ARG2=" CHARS" ;; * ) ARG2="" ;; esac echo echo "usage: $Name [option(s)]$ARG2 file(s)" echo echo "options: -r recurse" echo " -t test" echo " -f force" echo echo "(\"man lowercase\" for manpage)" echo exit } ######### # Error # ######### function Error () { echo "$Name: $*" >&2 exit 1 } ############## # renamefile # ############## function renamefile () { # arg(1) is space padding, arg(2) is filename arg(3) is the num # mv_lowercase: mv "FiLeName" -> "filename" # mv_uppercase: mv "filename" -> "FILENAME" # mv_capitalize: mv "filename" -> "Filename" # mv_underscore2hyphen: mv "file_name" -> "file-name" # mv_spaces2underscore: mv "file name" -> "file_name" # mv_prefix: mv "file_name" -> "$prefixfilename" # mv_suffix: mv "file_name" -> "filename$prefix" # mv_sed: mv "file_name" -> "script(filename)" # mv_chars2underscore: mv "file[:chars:]" -> "file_____" # mv_chars2null: mv "file[:chars:]" -> "file" # mv_number: mv "filename" -> "N" # mv_number.ext: mv "filename.ext" -> "N.ext" if [ ! -e "$2" -a ! -L "$2" ] ; then # links to nowhere are ok echo "${1}\"$2\" No such file" >&2 else case $Name in mv_lowercase|lowercase ) newname=`echo "$2" | tr '[:upper:]' '[:lower:]'` ;; mv_uppercase ) newname=`echo "$2" | tr '[:lower:]' '[:upper:]'` ;; mv_capitalize ) # ${ $2 : string offset (: length) } newname=`echo "${2:1}" | tr '[:upper:]' '[:lower:]'` newname=`echo "${2:0:1}" | tr '[:lower:]' '[:upper:]'`$newname ;; mv_underscore2hyphen ) newname=`echo "$2" | tr '_' '-'` ;; mv_spaces2underscore) newname=`echo "$2" | tr '[:blank:]' '_'` ;; mv_prefix ) newname="$Arg$2" ;; mv_suffix ) newname="$2$Arg" ;; mv_sed ) newname=`echo "$2" | sed -e "$Arg"` [ $? != "0" ] && Error "'sed' failed, exitting." ;; mv_chars2underscore ) newname=`echo "$2" | tr "$Arg" '_'` [ $? != "0" ] && Error "'tr' failed, exitting." ;; mv_chars2null ) newname=`echo "$2" | tr -d "$Arg"` [ $? != "0" ] && Error "'tr' failed, exitting." ;; mv_number ) newname=$3 ;; mv_number.ext ) # removes everything upto the last "." or does nothing if no "." tmp="${2##*.}" if [ "$2" == "$tmp" ] then newname="$3" else newname="$3.$tmp" fi;; *) Error "unresolved program link error !" ;; esac if [ "$2" != "$newname" ] ; then ### done in two stages ### as "mv FileA filea" fails on vfat ### - the TEST feature will wrongly report fails on vfat # this 'mv' failing generally indicate insufficient privilidges [ $TEST ] || if ! mv -- "$2" tmp$$; then Error "file mv error, bad :(" ; fi if [ ! -e "$newname" -a -n "$newname" ] ; then echo "${1}$2->$newname" if ! [ $TEST ] ; then mv -- tmp$$ "$newname" && total=$((total+1)) fi else if [ -n "$FORCE" -a -n "$newname" ] ; then if [ ! -d "$newname" -a ! -d tmp$$ ] ; then if ! [ $TEST ] ; then mv --backup=numbered -- tmp$$ "$newname" && total=$((total+1)) fi echo "${1}${2}->$newname (backup made)" else echo "${1}$2 FAIL source/dest is a directory" >&2 [ $TEST ] || mv -- tmp$$ "$2" fi else # not unique or forced or null, so fail and backtrack if [ -z "$newname" ] ; then echo "${1}$2 FAIL null dest" >&2 else echo "${1}$2 FAIL ($newname) not unique" >&2 fi [ $TEST ] || mv -- tmp$$ "$2" fi fi fi # names inequal fi # file exists } ########### # recurse # ########### function recurse () { ### arg(1) is space padding, arg(2) is dir local i= local num=1 for i in * ; do if [ -d "$i" -a ! -L "$i" ] ; then if cd "$i" ; then # check we have permission echo "$1$i:" recurse "$1${space}" "$i" cd .. fi fi renamefile "$1" "$i" "$num" num=$((num+1)) done } ######## # main # ######## Name=`basename $0` ### getopt can't handle the '-?' option, so have a special case [ "$1" == "-?" -o "$1" == "--help" ] && Usage shopt -s nullglob # expand "*" to null string space=" " # to change tabulation, change this variable ################### # process options # ################### FORCE= ; RECURSE= ; TEST= while getopts "frth" option ; do case $option in f ) FORCE=1 ;; r ) RECURSE=1 ;; t ) TEST=1 ;; h ) Usage ;; \? ) exit 1 ;; esac done shift $(($OPTIND - 1)) # OPTIND set by getopt ######################## # handle special cases # ######################## if [ "$Name" == mv_suffix -o "$Name" == mv_prefix -o "$Name" == mv_sed -o \ "$Name" == mv_chars2null -o "$Name" == mv_chars2underscore ] ; then Arg="$1" shift # If the 1st arg is a file they may have mussed up if [ -e "$Arg" -a -z "$FORCE" ] ; then echo "" echo "$Name: warning" echo "------------------" echo "The prefix/suffix must come first, but intended prefix/suffix \"$Arg\" is also a file" echo read -p "Continue (y/n) ? " reply [ "$reply" != "y" -a "$reply" != "Y" ] && exit fi fi if [ "$Name" == mv_suffix -o "$Name" == mv_prefix ] ; then echo "$Arg" | grep "/" > /dev/null && Error "Prefix/suffix must not contain a slash '/'." fi [ -z "$1" ] && Error "missing args(s)" ############# # main loop # ############# num=1 ; total=0 for j in "$@" ; do # ensure links are not dereferenced (see 'info mv') # bash trick to remove trailing slashes i=${j%/} current_dir=$PWD # these two lines are to allow "lowercase SomeDir/FileA" # else: "mv SomeDir/FileA somedir/filea" -> "mv:somedir , no such dir" # in most cases they do nothing # the added complexity probably outweighs any functional gain ## I don't think this 'cd' will ever fail, but if so, bail out ## - even if $i doesn't exist, it does nothing if cd "`dirname \"$i\"`" ; then file="`basename \"$i\"`" # if directory (and not a link) recurse it if RECURSE is set if [ -d "$file" -a -n "$RECURSE" -a ! -L "$file" ] ; then # check we have permission if cd "$file" ; then echo "$file:" recurse "$space" "$file" cd .. fi fi renamefile "" "$file" "$num" num=$((num+1)) cd "$current_dir" fi done echo "---------- $total file(s) renamed." ####### # end # #######