Search code examples
bashactive-directoryshsymlinksamba

Automatically create symlinks with lowercase names from uppercase directories


Thanks for considering to help.

I am attempting to create a simple shell script that once ran will look at all the directories of a folder that are typically in uppercase form and will create a symlink to that directory in lowercase form. Then i will execute this script on a regular basis as new folders are added to the directory to create additional symlinks to the new folders. The script has to be aware of other folders that have already had a symlink created for it so as not to create any stop issues or errors. Below is a visualization of what i am attempting to achieve.

drwxr-xr-x 2 root root 4096 Jun 18 03:52 Holmes
drwxr-xr-x 2 root root 4096 Jun 18 03:52 Kingship
-rwxrwxrwx 1 root root   85 Jun 18 04:39 link.sh
drwxr-xr-x 2 root root 4096 Jun 18 03:52 Lost
drwxr-xr-x 2 root root 4096 Jun 18 03:52 Monkey
drwxr-xr-x 2 root root 4096 Jun 18 03:52 Space Ship
drwxr-xr-x 2 root root 4096 Jun 18 03:52 Test

to

lrwxrwxrwx  1 root root    6 Jun 18 04:49 holmes -> Holmes
drwxr-xr-x  2 root root 4096 Jun 18 03:52 Holmes
lrwxrwxrwx  1 root root    8 Jun 18 04:49 kingship -> Kingship
drwxr-xr-x  2 root root 4096 Jun 18 03:52 Kingship
-rwxrwxrwx  1 root root   85 Jun 18 04:39 link.sh
lrwxrwxrwx  1 root root    4 Jun 18 04:49 lost -> Lost
drwxr-xr-x  2 root root 4096 Jun 18 03:52 Lost
lrwxrwxrwx  1 root root    6 Jun 18 04:49 monkey -> Monkey
drwxr-xr-x  2 root root 4096 Jun 18 03:52 Monkey
lrwxrwxrwx  1 root root   10 Jun 18 04:49 space ship -> Space Ship
drwxr-xr-x  2 root root 4096 Jun 18 03:52 Space Ship
lrwxrwxrwx  1 root root    4 Jun 18 04:49 test -> Test
drwxr-xr-x  2 root root 4096 Jun 18 03:52 Test

Obviously the script must be capable of creating new symlinks for folders that are created later but don't yet have a symlink for them.

I've been tinkering with the syntax from these other two posts/answers and am not really getting anywhere other then recreating the results of the individual answers. Posts are here Using find on subdirectories and create symlinks to all files and Bash script to automatically create symlinks to subdirectories in a tree

Perhaps i am going about my solution with the wrong approach. What i am actually trying to achieve is the fact that this is a NAS system that has folders automatically created based on users names. The folders are then automatically shared via the samba homes share. However the shares are not accessible unless a lowercase folder is created since the folders are originally created using spaces and as well using the case structure of the username. The structure of the username isn't totally compatible with a unix system since Test and test on a unix system are two different things. I've researched how to allow usernames to be created using uppercase however making those changes doesn't effect how the samba shares recognize the folders.


Solution

  • A step-by-step way:

    First, in a scratch directory, I'm creating a sample of directories like yours:

    $ mkdir Holmes Kingship Lost Monkey 'Space Ship' Test a_lower_case_one
    $ touch just_a_file One_with_UpperCase
    $ ln -s Holmes holmes
    $ ls -og
    total 28
    drwxr-xr-x 2 4096 Jun 18 11:54 a_lower_case_one
    lrwxrwxrwx 1    6 Jun 18 11:54 holmes -> Holmes
    drwxr-xr-x 2 4096 Jun 18 11:54 Holmes
    -rw-r--r-- 1    0 Jun 18 11:54 just_a_file
    drwxr-xr-x 2 4096 Jun 18 11:54 Kingship
    drwxr-xr-x 2 4096 Jun 18 11:54 Lost
    drwxr-xr-x 2 4096 Jun 18 11:54 Monkey
    -rw-r--r-- 1    0 Jun 18 11:54 One_with_UpperCase
    drwxr-xr-x 2 4096 Jun 18 11:54 Space Ship
    drwxr-xr-x 2 4096 Jun 18 11:54 Test
    

    Now, how are you going to loop through your directories? Like so:

    $ for i in */; do echo "$i"; done
    a_lower_case_one/
    holmes/
    Holmes/
    Kingship/
    Lost/
    Monkey/
    Space Ship/
    Test/
    

    How do you convert a variable in lower case in bash? According to this section of the bash reference manual, the expansion of ${i,,} will be that of $i in lowercase:

    $ for i in */; do echo "$i -> ${i,,}"; done
    a_lower_case_one/ -> a_lower_case_one/
    holmes/ -> holmes/
    Holmes/ -> holmes/
    Kingship/ -> kingship/
    Lost/ -> lost/
    Monkey/ -> monkey/
    Space Ship/ -> space ship/
    Test/ -> test/
    

    Now we can only consider the dir names that are different from their lower case expansion:

    $ for i in */; do [[ $i = ${i,,} ]] && continue; echo "$i -> ${i,,}"; done
    Holmes/ -> holmes/
    Kingship/ -> kingship/
    Lost/ -> lost/
    Monkey/ -> monkey/
    Space Ship/ -> space ship/
    Test/ -> test/
    

    For each of these, we'll test whether there's already a lower case one existing:

    $ for i in */; do [[ $i = ${i,,} ]] && continue; echo "$i -> ${i,,}"; if [[ -e ${i,,} ]]; then echo "    ${i,,} exists"; fi; done
    Holmes/ -> holmes/
        holmes/ exists
    Kingship/ -> kingship/
    Lost/ -> lost/
    Monkey/ -> monkey/
    Space Ship/ -> space ship/
    Test/ -> test/
    

    When the lower case one already exists, we'll check whether it's a link linking to the upper case one. If yes, all is good, otherwise we'll print a warning. Because it's getting a bit large, I'm going to write it as a bash script:

    #!/bin/bash
    
    for i in */; do
        d=${i%/}
        dl=${d,,}
        [[ $d = $dl ]] && continue
        if [[ -e $dl ]]; then
            if [[ ! -L $dl ]]; then
                echo "    ERROR: $dl is not a link"
            elif [[ $(readlink -- "$dl") != $d ]]; then
                echo "    ERROR $dl exists and doesn't point to $d"
            fi
        else
            ln -sv -- "$d" "$dl"
        fi
    done
    

    I've used all the best practices I know in order to have a robust script, safe regarding funny symbols (spaces, hyphens, etc...) in file names.

    Note. Must be called from within the directory you want to process. Use cd at the top of this script if necessary.

    Hope this helps!