Search code examples
macosterminalapplescriptzsh

Replace first character of file in folder from Uppercase to Lower case


I'm trying to convert 3,000 or so .svg files from CapitalCase to camelCase.

Current:

-Folder
--FileName1
--FileName2
--FileName3

Goal:

-Folder
--fileName1
--fileName2
--fileName3

How can I use terminal to change the casing on the first character with to lowercase?

Currently I've been trying something along these lines: for f in *.svg; do mv -v "$f" "${f:1}"; done

All files in the folder start with a letter or number.


Solution

  • This can be done very succinctly in zsh with zmv:

    autoload zmv
    zmv -nvQ '(**/)(?)(*.svg)(.)' '$1${(L)2}$3'
    

    This will recurse through any number of directory levels, and can handle name collisions and other edge cases. Some of the pieces:

    • -n: no execution. With this option, zmv will only report what changes it would make. It's a dry run that can be used to test out the patterns.
      Remove the -n when you're ready to actually change the names.
    • -v: verbose.
    • -Q: qualifiers. Indicates that the source pattern has a glob qualifier (in our case (.)).
    • '(**/)(?)(*.svg)(.)': source pattern. This is simply a regular zsh glob pattern, divided into groups with parentheses. The underlying pattern is **/?*.svg(.). The pieces:
      • (**/): directories and subdirectories. This will match any number of directory levels (to only affect the current directory, see below).
      • (?): matches a single character at the start of the file name. We'll convert this to lowercase later.
      • (*.svg): matches the rest of the file name.
      • (.): regular files only. This is a zsh glob qualifier; zmv recognizes it as a qualifier instead of a grouping because of the -Q option. The . qualifier limits the matching to regular files so that we don't try to rename directories.
    • '$1${(L)2}$3': destination pattern. Each of the groupings in the source pattern is referenced in order with $1, $2, etc.
      • $1: the directory. This could contain multiple levels.
      • ${(L)2}: The first letter in the file name, converted to lowercase. This uses the L parameter expansion flag to change the case.
        The l expansion modifier will also work: $2:l.
        The conversion can handle non-ASCII characters, e.g. Éxito would become éxito.
      • $3: the rest of the file name, including the extension.

    Variations

    This will only change files in the current directory:

    zmv -nv '(?)(*.svg)' '$1:l$2'
    

    The source pattern in the following version will only match files that start with an uppercase letter. Since the zmv utility won't rename files if the source and destination match, this isn't strictly necessary, but it will be slightly more efficient:

    zmv -nvQ '(**/)([[:upper:]])(*.svg)(.)' '$1${(L)2}$3'
    

    More information