Regarding this problem and answer: https://unix.stackexchange.com/a/188020/189709

for f in *; do mv --backup=numbered -- "$f" "${f,,}"; done
rename -v -- 's/\.~(\d+)~/$1/' *.~*~

mv: This command implicitly changes all filenames to lowercase. How to modify the options not to do that? The backup numbering is sufficient to distinguish the files.

rename: test.~1~ won't be renamed if a file test1 already exists. How to change the options to have test.~1~ renamed to test2?

  • I'm not quite sure what you want to achieve. The filenames are lowercased explicitly by ${f,,}.
    – Kusalananda
    Nov 7 '19 at 9:11
  • @Kusalananda I assume the OP was unaware of how the "implicit" Bash string manipulation syntax ${VAR<modifiers>} works ...
    – AdminBee
    Nov 7 '19 at 9:16
  • @Kusalananda So how to change the destination operand of mv in order to have duplicated filenames renamed to .~1~ .~2~ .~3~ etc. without changing the filenames to lowercase? AdminBee is true, I am unaware of the bash sting manipulition syntax, that's why I ask
    – jamacoe
    Nov 7 '19 at 9:57
  • Oh, I see what you're getting at now. Good. Hmm... Could you show what your would want happen with the filenames test, Test, and TEST for example?
    – Kusalananda
    Nov 7 '19 at 10:14
  • Given a directory with three files: Test1 Test test .I want to mount this NTFS volume on Windows Home (i.e. not supporting fsutil.exe file setCaseSensitiveInfo C:\folder enable). So I need unique filenames without case distinguation. Step 1 (the mv command) renames the files to test1 test.~1~ test .This command should not change cases. Step 2 (rename) changes the backup numbering to a plain number at the end of the filename. Here it atempts to change test.~1~ to test1 It fails because such a file already exists. So test11 or test2 would be feasible.
    – jamacoe
    Nov 7 '19 at 10:26

You may want to implement your renaming strategy yourself since the code you are looking at explicitly lower-cases the filenames in the call to mv to catch duplicates in actual filename collisions.

The following only renames files if there are collisions, without actually causing the collisions (i.e. it does not fall back to GNU mv to handle the collisions):


# The suffix array contains a filename suffix (a positive integer)
# for each lower-cased filename.
declare -A suffix

# Get all names in the current directory.
set -- *

# Initialise the suffixes for each existing name to zero.
for name do

for name do

        # Loop until no name collision.
        while true; do
                # Get the current suffix for this filename.

                # Increment the suffix for this filename.
                suffix[${name,,}]=$(( ${suffix[${name,,}]} + 1 ))

                # If the suffix is 0, empty it.
                [[ $suf == 0 ]] && suf=

                # Create the new name.

                # Break out of the loop if the name didn't change, or if
                # there's no other file that has this name.
                if [[ $name == "$newname" ]] ||
                   [[ -z ${suffix[${newname,,}]} ]]

        if [[ $name != "$newname" ]]; then
                printf '%s --> %s\n' "$name" "$newname"
                # uncomment the next line to actually do the renaming
                # mv -- "$name" "$newname"
                printf '%s unchanged\n' "$name"


(note the commented out mv, you may want to run this with that line commented out until you are confident that it does the right thing)


$ ls
A         Bb        TEST1     a         bb        test
BB        TEST      Test      bB        script.sh
$ bash script.sh
A unchanged
BB unchanged
Bb --> Bb1
TEST unchanged
TEST1 unchanged
Test --> Test2
a --> a1
bB --> bB2
bb --> bb3
script.sh unchanged
test --> test3
  • Thank you @kusalananda, you are a real bash pro!
    – jamacoe
    Nov 8 '19 at 7:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.