3

I am attempting to rename all directories and files to uppercase with a shell script. What I have works, but not for sub directories. As the directory names are changing during the scripts execution I get things like mv: cannot stat './def/two/three': No such file or directory

I have tried using -depth with find so it would rename from the bottom up. But still run into the same problem. I though about using cut to break apart the path on / and rename that way, but am at a loss.

Here's what I have:

for i in `find . -name "*[a-z]*"`
    do new_name=`echo $i | tr '[a-z]' '[A-Z]'`
    mv $i $new_name
done

I would appreciate any direction as I feel like this should be a common task, but failed to find a working solution from some Google searches.

Please note, I can not use rename as it not supported by my distro.

Jason McCreary
  • 71,546
  • 23
  • 135
  • 174
  • I propose a move to SuperUser. – Teddy Nov 24 '10 at 16:01
  • 1
    If you have Perl, `rename` is an extremely simple Perl script. You can use it instead of the binary (which is not as powerful). There are dozens of questions on SO about renaming files and directories and even a few about doing it [depth-first](http://stackoverflow.com/search?q=%2Brename+%2Bdepth). – Dennis Williamson Nov 24 '10 at 17:16
  • possible duplicate of [How to rename all folders and files to lowercase on Linux?](http://stackoverflow.com/questions/152514/how-to-rename-all-folders-and-files-to-lowercase-on-linux) – Dennis Williamson Nov 24 '10 at 17:17
  • See also [Merge and lowercase Directories and FIles](http://superuser.com/questions/175548/merge-and-lowercase-directories-and-files). – Gilles 'SO- stop being evil' Nov 24 '10 at 23:53
  • Related question: [Better way to rename files based on multiple patterns](http://stackoverflow.com/a/25597051/2654678). – Michaël Le Barbier Aug 31 '14 at 23:12

3 Answers3

8

Try this way :

find . -depth |while read LONG; do SHORT=$( basename "$LONG" | tr '[:lower:]' '[:upper:]' ); DIR=$( dirname "$LONG" ); if [ "${LONG}" != "${DIR}/${SHORT}"  ]; then mv "${LONG}" "${DIR}/${SHORT}" ; fi; done

or, if you want the readable version (no one-liner) :

find . -depth | \
while read LONG; do
   SHORT=$( basename "$LONG" | tr '[:lower:]' '[:upper:]' )
   DIR=$( dirname "$LONG" )
   if [ "${LONG}" != "${DIR}/${SHORT}"  ]; then
     mv "${LONG}" "${DIR}/${SHORT}"
   fi
done

This will rename files before, then the directory they're in, in the proper order.

Baramin
  • 1,359
  • 10
  • 12
  • You need to quote *all* the $(FOO) and $FOO like "$(FOO)" and "$FOO". – Teddy Nov 24 '10 at 16:00
  • @Teddy: you're right, I had 2 $LONG that needed to be quoted. It's fixed now. I don't think I missed any. – Baramin Nov 24 '10 at 16:05
  • This works. Note that if the `$LONG` and `${DIR}/${SHORT}` match, `mv` will throw an error. For future stumblers, adding a comparison check would solve this problem. – Jason McCreary Nov 24 '10 at 17:39
  • There, fixed it so you're not getting the error message if the name is already 100% uppercase. – Baramin Nov 24 '10 at 19:33
  • @Baramin: No, you still need to quote all your $( foo bar baz ) as "$( foo bar baz )". – Teddy Jan 17 '11 at 09:33
  • I doubt it... if you do MYVAR=$( echo a b c ) you do get a single variable containing "a b c" without needing the quotes. Or maybe I don't understand what you said. – Baramin Jan 27 '11 at 15:06
  • Thanks all! I replaced the tr command: `tr '[:lower:]' '[:upper:]'` with, for example: `sed s/dessert/desert/g` to do word replacement. – JJones Dec 11 '13 at 09:02
0

lets say you have this directory structure
/somewhere/somethimg/
/somewhere/somethimg/dir1
/somewhere/somethimg/dir1/dir2

I think you should start the rename with dir2 then go to dir1 and so on

cristian
  • 8,676
  • 3
  • 38
  • 44
0

This script should rename all files/directories in current path in "leaf" first order to make things work.

#!/bin/bash

IFS=$'\n';
TMP=/tmp/file_list
rm -f ${TMP};

for file in `find .`
do
num=`echo $file | grep -o  "/" | wc -l`;
echo "$num $file" >> ${TMP};
done

sort -n -r ${TMP} | cut -f 2 -d ' ' > ${TEMP}.temp;

for file in `cat ${TEMP}.temp`
do
echo $file;
## Code to rename file here. All subdirectories would already have been renamed
done
  • +1 for the additional example. I did not thoroughly test it, but it seems to work. You do need to change `$TEMP` to `$TMP` though. – Jason McCreary Nov 24 '10 at 17:48
  • This will choke on file names containing special characters (whitespace, `\[*?`). It's also not production-ready due to the insecure use of a temporary file. To start with, always use double quotes around variable substitutions (`$var`) and command substitutions (`\`command\``). Use `find -exec`, that's what it's for. The sort step is useless, read up on `find -depth`. – Gilles 'SO- stop being evil' Nov 24 '10 at 23:57
  • Yup, didnt know the -depth option. –  Nov 25 '10 at 05:01