2

I'm trying to format old files to fit/incorporate in our current automation of dataset processing. However, the directory and sub-directory names of the old files where all in upper case.

I don't exactly know how to do this using bash script. Is there a way to change all directory and sub-directory names to lower case?

ckelmeckis
  • 43
  • 3
  • Do you want to rename files too, or just directories? eg. Should `DIR2/DIR3/FILE4` become `dir2/dir3/FILE4` or `dir2/dir3/file4` ? – jhnc Jan 16 '23 at 21:52
  • Do you care about non-ASCII characters in names? eg. Should `ABCÄÜÏÖËŸ` become `abcäüïöëÿ` ? – jhnc Jan 16 '23 at 21:59
  • If directories `AAA`, `AaA` and `aAa` exist, what should happen? – jhnc Jan 16 '23 at 22:09
  • @jhnc i just need to rename the directories. the filenames are fine. there are no non-ASCII names and if it's mixed case, i need it to be just lowercase. – ckelmeckis Jan 17 '23 at 00:03
  • So `AAA` becomaes `aaa` and `AaA` becomes the same `aaa` and `aAa` becomes the same `aaa`. Does that mean you want the contents of all three to be merged together? – jhnc Jan 17 '23 at 04:02
  • Also consider `DDD/file` and `DdD/file`. If both `DDD/` and `DdD/` merge to become a single new `ddd/`, then there will be two `ddd/file`. What should happen to the extra? – jhnc Jan 17 '23 at 06:18

4 Answers4

3

If you want to change the directories and subdirectories only (i.e. leave the files alone), you should go with the find command.

If you are using bash v. 4 or greater you can do it without invoking any other program but bash itself:

$ find . -mindepth 1 -depth -type d -execdir bash -c \
    'mv -T "${0}" "${0,,}"' "{}" \;

(edited to add the -T option, see comments).

Otherwise, you need to convert the directory name by other means, e.g. tr:

$ find . -mindepth 1 -depth -type d -execdir bash -c \
    'mv -T "$0" "$(echo $0 | tr [:upper:] [:lower:])"' "{}" \;

(edited to add double quotes around paths, as suggested in the comments)

Note that you must use -depth in order to change the directory names in depth-first order (do not cut the branch you are sitting on).

Also, you need -execdir instead of -exec to rename just one path element at a time.

David
  • 166
  • 6
  • 1
    With your second command, you forgot to doublequote `$0` and `$ln` usage. Consider a directory named `A B C` – jhnc Jan 16 '23 at 22:17
  • Note that if you have two directories, `AAA` and `AAa`, this code will move one of them to `aaa` and the other inside it (either `aaa/AAA` or `aaa/AAa`) – jhnc Jan 17 '23 at 06:13
  • 1
    Absolutely correct. I assumed, as the OP stated, that the names "were all in upper case". To cope with all the general cases a script would fit better than a one-liner. – David Jan 17 '23 at 09:53
  • 1
    Still, you can make it more robust by using the `-T` option of `bash`'s `mv` command (treat DEST as a normal file). I just found out about it thanks to your comment. – David Jan 17 '23 at 16:13
  • You missed a `$0` and single-quotes can't be nested. `-T` isn't portable (I can only find it in the coreutils version of `mv`) but when available could be very useful, nice find. (It's not part of bash, btw) – jhnc Jan 17 '23 at 22:32
  • Does the `echo $0` really need quotes? I have them around the whole subcommand, it should be enough. The single quotes are actually a mistake, but they don't harm because `tr` don't need them. I'll remove them for clarity anyway. For the `-T` you are right, I was assuming that `mv` was a built-in command, but that's not the case... – David Jan 19 '23 at 09:25
  • `echo $0` compacts characters in `$IFS`. eg. `A B C` will be renamed to `a b c`. Also, in general, not quoting `tr` arguments could cause subtle bugs. eg. try `mkdir 'A B C' w` and then run your second command – jhnc Jan 19 '23 at 11:28
2

This may come handy:

$ var=AAA
$ echo ${var,}
aAA
$ echo ${var,,}
aaa

And to upper:

$ var=aaa
$ echo ${var^}
Aaa
$ echo ${var^^}
AAA
Ivan
  • 6,188
  • 1
  • 16
  • 23
0

tr may be of help, as in

echo HELLO | tr '[:upper:]' '[:lower:]'

So you may do something similar to

for i in $(ls)
do
  mv $i $(echo $i | tr '[:upper:]' '[:lower:]')
done
Knut Forkalsrud
  • 1,134
  • 1
  • 7
  • 19
  • 4
    Parsing output of `ls` is bad practice. The `for` will fail if directory names contain any character in `$IFS` (eg. space). It is good practice to quote variables (eg. `"$i"`) – jhnc Jan 16 '23 at 22:14
0

A pure Bash find based concept

Based on Knut's solution I would propose you to extend to the snippet below and build upn:

find . -maxdepth 1 -exec echo echo {} \| tr "[:upper:]" "[:lower:]" \;

find . -maxdepth 1 -exec echo mv {} \$\(echo {} \| tr "[:upper:]" "[:lower:]"\) \;

This is going to build and show you the operations needed to create the lower case named folders. You could use the output as a template for your renaming script, or wrap it in a renaming function for instance.

enter image description here

Variations of the same command for higher flexibility:

  • find .: the period is going to search in the local directory you are in. If you'd like to analyse another folder, just replace the dot with the path to that folder. Example: $ find /path/to/another

  • -maxdepth 1: This is the depth the find command should take into consideration. 1 means, apply only on the subfolders of this folder (recursion level 1). -maxdepth 2 means, go into every subfolder and do the same (recursion level 2). ...

Mel Te
  • 42
  • 4