Search code examples
bashloopstestingconditional-statementsmatching

Bash: Iteratively Mount First Drive, Volume Or Partition Matching A Naming Scheme, Ignoring Second and Subsequent Matches


I want to:

  1. iterate over a list;
  2. find the first instance in which there is a matching substring;
  3. do something about that;
  4. ignore any subsequent matches.
  5. continue to the end of the list.

More concretely:

#! /bin/bash

target_ORG=$(ls /usr/local/etc/ini/system/ORG)
target_DOMAIN=$(ls /usr/local/etc/ini/system/DOMAIN)
target_SITE=$(ls /usr/local/etc/ini/system/SITE)
target_DEVICE=$(ls /usr/local/etc/ini/system/DEVICE)
target_VOLUME=$(ls /usr/local/etc/ini/system/VOLUME)
target_HOST=$(ls /usr/local/etc/ini/system/HOST)


instanceList=$(ls /dev/disk/by-partlabel)

for i in $instanceList 
 
  do
    instance_ORG=${i:0:3}
    instance_DOMAIN=${i:4:3}
    instance_SAN=${i:8:1}
    instance_SITE=${i:10:1}
    instance_DEVICE=${i:12:5}
    instance_VOLUME=${i:18:6}
    
    <PSEUDOCODE>
   
    Compare the instance_* strings with the target_* strings
    IF they all match
       THEN
           mount $i /path/to/mountpoint

    </PSEUDOCODE>

  done

Where:

  1. the specific devices available vary across time

  2. the partlabel format is: xxx-xxx-x-x-xxxxx-xxxxxx (e.g. 001-001-1-1-00001-000001), indicating a disk partition as being ORG-DOMAIN-SAN-SITE-DEVICE-VOLUME

  3. the /usr/local/etc/ini/system/* directories each contain a file named for the corresponding substring - allowing changes to be made by simply renaming (rather than having to manipulate the content of) files

  4. /path/to/mountpoint is the mountpoint for the matching $instance_SAN

That is ... 

xxx-xxx-1-x-xxxxx-xxxxxx mounts to /path/to/1
xxx-xxx-2-x-xxxxx-xxxxxx mounts to /path/to/2
xxx-xxx-3-x-xxxxx-xxxxxx mounts to /path/to/3


The problem with the for loop is that it will simply assign the next value to the $instance_* variable, when I want it to keep the first found instance - so, xxx-xxx-1-1-xxxxx-xxxxxx will be overmounted with xxx-xxx-1-2-xxxxx-xxxxxx and so on.


A case test will find and keep the first found instance, but won't then iterate over the rest of $instanceList

e.g. given

/usr/local/etc/ini/system/ORG/001
/usr/local/etc/ini/system/DOMAIN/001
/usr/local/etc/ini/system/SITE/1
/usr/local/etc/ini/system/DEVICE/00001
/usr/local/etc/ini/system/VOLUME/000001
/usr/local/etc/ini/system/HOST/000001

...and...

*$instanceList* of:

001-001-1-1-00001-000001
001-001-1-1-00001-000002
001-001-1-2-00001-000001
001-001-1-2-00001-000002
001-001-2-2-00001-000001
001-001-2-2-00002-000001
001-001-3-5-00001-000002

001-001-1-1-00001-000001 will get mounted to /path/to/1
and everything else will be ignored.


I need to mount the first matching device and ignore any others with a matching $instance_SAN, whilst continuing to iterate across $instanceList.

e.g. given the above 

001-001-1-1-00001-000001 should be mounted to /path/to/1
001-001-1-1-00001-000002 should be ignored
001-001-1-2-00001-000001 should be ignored
001-001-1-2-00001-000002 should be ignored
001-001-2-2-00001-000001 should be mounted to /path/to/2
001-001-2-2-00002-000001 should be ignored
001-001-3-5-00001-000002 should be mounted to /path/to/3


Nested if tests are too brittle.

Multiple case tests could work

<PSEUDOCODE>
 
Case of $instance_SAN=1: mount device/partion to /path/to/$instance_SAN
Case of $instance_SAN=2: mount device/partion to /path/to/$instance_SAN
Case of $instance_SAN=3: mount device/partion to /path/to/$instance_SAN

</PSEUDOCODE>

But I just can't wrap my head around how to iterate across $instanceList without simply breaking after the first match.

I realise this is probably very basic, but my bash just isn't very advanced, I'm afraid.

I'd like to avoid solutions such as autoFS, however, so that the approach isn't reliant upon anything more than a core Linux install; even something like a minimal Arch/Gentoo, or even LFS, should be able to provision everything without the need for any extra utilities - also, this has to be done pre-login, so, I don't think a 'third party' solution would work anyway.

=======
UPDATE
=======

Following the suggestion, by pjh, to try mountpoint || mount ...

#! /bin/bash

target_ORG=$(ls '/usr/local/etc/ini/system/ORG')
target_DOMAIN=$(ls 'usr/local/etc/ini/system/DOMAIN')
target_SITE=$(ls '/usr/local/etc/ini/system/SITE')
target_DEVICE=$(ls '/usr/local/etc/ini/system/DEVICE')
target_VOLUME=$(ls '/usr/local/etc/ini/system/VOLUME')
target_HOST=$(ls '/usr/local/etc/ini/system/HOST')


echo "target_ORG=$target_ORG"
echo "target_DOMAIN=$target_DOMAIN"
echo "target_SITE=$target_SITE"
echo "target_DEVICE=$target_DEVICE"
echo "target_VOLUME=$target_VOLUME"
echo "target_HOST=$target_HOST"


instanceList=$(ls /dev/disk/by-partlabel)


for i in {1..3}

    do

      for n in $instanceList 

        do

          echo -e '\n'
          echo "i = $i"
          echo "n = $n"

          instance_ORG=${n:0:3}
          instance_DOMAIN=${n:4:3}
          instance_SAN=${n:8:1}
          instance_SITE=${n:10:1}
          instance_DEVICE=${n:12:5}
          instance_VOLUME=${n:18:6}

          mountpoint "/mnt/test/$i" || mount "$n" "/mnt/test/$i"

      done
done


... results in

target_ORG=000
target_DOMAIN=000
target_SITE=1
target_DEVICE=00000
target_VOLUME=000001
target_HOST=000001


i = 1
n = 000-000-1-1-00000-000001
/mnt/test/1 is not a mountpoint
mount: /mnt/test/1: special device 000-000-1-1-00000-000001 does not exist.

i = 1
n = 000-000-2-1-00000-000001
/mnt/test/1 is not a mountpoint
mount: /mnt/test/1: special device 000-000-2-1-00000-000001 does not exist.

i = 1
n = 000-000-3-1-00000-000001
/mnt/test/1 is not a mountpoint
mount: /mnt/test/1: special device 000-000-3-1-00000-000001 does not exist.


i = 2
n = 000-000-1-1-00000-000001
/mnt/test/2 is not a mountpoint
mount: /mnt/test/2: special device 000-000-1-1-00000-000001 does not exist.

i = 2
n = 000-000-2-1-00000-000001
/mnt/test/2 is not a mountpoint
mount: /mnt/test/2: special device 000-000-2-1-00000-000001 does not exist.

i = 2
n = 000-000-3-1-00000-000001
/mnt/test/2 is not a mountpoint
mount: /mnt/test/2: special device 000-000-3-1-00000-000001 does not exist.


i = 3
n = 000-000-1-1-00000-000001
/mnt/test/3 is not a mountpoint
mount: /mnt/test/3: special device 000-000-1-1-00000-000001 does not exist.

i = 3
n = 000-000-2-1-00000-000001
/mnt/test/3 is not a mountpoint
mount: /mnt/test/3: special device 000-000-2-1-00000-000001 does not exist.

i = 3
n = 000-000-3-1-00000-000001
/mnt/test/3 is not a mountpoint
mount: /mnt/test/3: special device 000-000-3-1-00000-000001 does not exist.

But they very much do exist - which is proven by simply manually mounting them to the same locations (so, it doesn't appear to be a problem of the kernel not being aware of them).

And ls -l /dev/disk/by-partlabel lists a b the first column - so it doesn't seem to be that they aren't recognised as block devices either.

Am I running into the issue referred to by Cyrus? (I can't find anything else that seems to hint at the cause).


FWIW ...
DMESG reports some errors

[ 7064.170224] pcieport 0000:00:1c.0: AER: Corrected error received: 0000:00:1c.0
[ 7064.170242] pcieport 0000:00:1c.0: PCIe Bus Error: severity=Corrected, type=Physical Layer, (Receiver ID)
[ 7064.170247] pcieport 0000:00:1c.0:   device [8086:a115] error status/mask=00000001/00002000
[ 7064.170254] pcieport 0000:00:1c.0:    [ 0] RxErr                  (First)
...
[11372.230786] 000-000-1-1-00000-000001: Can't open blockdev
[11372.232977] 000-000-2-1-00000-000001: Can't open blockdev
[11372.235242] 000-000-3-1-00000-000001: Can't open blockdev


After the final `done`, I tried adding
export "$n"

The result was

export: '000-000-3-1-00000-000001': not a valid identifier

Changing the PARTLABEL to remove the '-' symbols resulted in

export: '0000003100000000001: not a valid identifier

So, unless my export is incorrect, it's not those either.

Changed it to

export N="$n"

and

export "N=$n"

There was no error this time, but echoing $N returned a null output.


Solution

  • [SOLUTION]

    #! /bin/bash
    
    target_ORG=$(ls '/usr/local/etc/ini/system/ORG')
    target_DOMAIN=$(ls '/usr/local/etc/ini/system/DOMAIN')
    target_SITE=$(ls '/usr/local/etc/ini/system/SITE')
    target_DEVICE=$(ls '/usr/local/etc/ini/system/DEVICE')
    target_VOLUME=$(ls '/usr/local/etc/ini/system/VOLUME')
    target_HOST=$(ls '/usr/local/etc/ini/system/HOST')
    
    
    instanceList=$(ls '/dev/disk/by-partlabel')
    
    
    for i in {1..3} ; do
    
          for n in $instanceList ; do
    
              instance_ORG=${n:0:3}
              instance_DOMAIN=${n:4:3}
              instance_SAN=${n:8:1}
              instance_SITE=${n:10:1}
              instance_DEVICE=${n:12:5}
              instance_VOLUME=${n:18:6}
    
              mountpoint -q /path/to/$i || mount /dev/disk/by-partlabel/$instance_ORG\-$instance_DOMAIN\-$i\-$instance_SITE\-$instance_DEVICE\-$instance_VOLUME /path/to/$i >/dev/null 2>&1
    
          done
    done
    

    To specify a specific subset from the available devices, substitute $target_* for $instance_* in the mount command