Jonathan
Jonathan

Reputation: 539

How to do a case insensitive for loop in shell scripting

I'm trying to make a loop to create directories and move files into those directories so that I can sort stuff alphabetically. Here's what I have:

for i in a b c d e f g h i j k l m n o p q r s t u v w x y z
    do
        mkdir $i
        mv -i $i*.* ./$i/
done
ls

The issue is that the mv command in this loop doesn't catch the uppercase file names, and I don't want to create directories for both upper and lower case file names. What's the solution? Or if you don't want to come right out and tell me the solution, where can I find it?

I've looked at a few things on google and I haven't found a solution that I could use. I'm relatively new to shell scripting, so please explain any solutions you may suggest so that I'll understand and don't have to ask a similar question later.

Upvotes: 4

Views: 3568

Answers (2)

simont
simont

Reputation: 72637

In zsh you can use zmv. [I can't link directly to the man page, but it's in man zshcontrib].

You'll need to load it using autoload -U zmv before using it.

It's quite powerful. To solve your problem:

zmv -n '([a-zA-Z])(*)' '${(L)1}/${1}${2}'

To explain:

  • zmv -n: the -n flag means "don't do this, just display what you'd do if I didn't tell you not to do it".
  • The first part is our matching pattern. We want to match two parts of the filename, which we do in (..) (one for each match). [If we omit the brackets, zsh will still match, but we won't be able to refer to that match later]. We match the first character if it's an alphabetical, and everything else.
  • The second half is what we'll move the file to. We take the first matched glob (which is the first character): ${<number>}, (where <number> is the index of the glob matched in the first part) and convert it to lowercase with (L). We then add a / to the filename, which will place the file into a directory for us. Finally, we write back the filename in two parts: the unmodified first character, and everything else ${1}${2}

For more examples, check here. This page has some more basic information.

You can, if you're careful, rename hundreds of files in lots of subdirectories in a single line using **. For example:

zmv -n "(**/)(*). TV-Show-Name-(*)-S(*).avi" '${1}S0${4}E${2} - ${3}.avi'

Will rename every avi file in every subdirectory of the current directory. The hardest part of this is determining the regex to match the files you want.

Please use -n to check that the command you're about to run does what you think it does.

Upvotes: 1

that other guy
that other guy

Reputation: 123560

In bash, you can use

shopt -s nocaseglob

Upvotes: 8

Related Questions