3

I am trying to find a hard drives manufacture/model based on the UUID that I know of a hard drive in a command.

I tried using lsblk -o FSTYPE,MOUNTPOINT,UUID,MODEL,SIZE to list all the hard drives and then store them in an array to find the UUID and its model. However the mounted hard drive's UUID never contains the model information of the hard drive.

It only lists the model hdd on the root location of a hdd ie /sda.

I am writing a bash script that stores the UUID and the manufacture/model of the hdd as well as the file system type so I can then automatically mount the hdd's that have a valid file system type on a folder prefix of /someLocation/${model}-${UUID-first-4-char}

The below creates two arrays at the moment, one for uuid and one for the file type.

 #create array
    fsTypeArray=()
    uuidArray=()
    mapfile -t fsTypeArray < <(lsblk -o FSTYPE,MOUNTPOINT,UUID | awk 'NF==2 {print $1}')
    mapfile -t uuidArray < <(lsblk -o FSTYPE,MOUNTPOINT,UUID | awk 'NF==2 {print $2}')

And then here is the if statement that either mounts ext4 or ntfs

index=0
    for i in "${fsTypeArray[@]}"
    do
        if [ $i == ntfs ] 
        then
            echo "mountNtfsDrive"
            currentDate="date +%Y%m%d%H%M%S"
            mountNtfsDrive "${uuidArray[$index]}" `$currentDate`
        elif [ $i == ext4 ]
        then
            echo "mount ext4" 
            currentDate="date +%Y%m%d%H%M%S"
            mountExt4Drive "${uuidArray[$index]}" `$currentDate`
        fi
        echo index
        let "index++"
         sleep 1s

I want to replace and remove hat currentDate into basically ${MODEL/MAKE-UUID(first 4 characters)}.

The currentDate basically uses the current date and time as a folder name but I want it to consistently create/mount on the same folder that is unique to that hdd's UUID and hard drive make/model.

eg ; mount UUID=1234567 /media/ext/western-digital-1234

Below are the two functions that mount the hard drives:

# Mount a ext4 drive
function mountExt4Drive {
    uuid=$1
    location=$2
    mkdir -pv "/home/$location"
    sleep 3s
    echo "mounting ext4 hdd $uuid in $location"
    mount UUID=$uuid $location
}

# Mount NTFS drive
function mountNtfsDrive {
    echo "mounting ntfs $1 in $2"
    mkdir -pv "/home/$2"
    sleep 3s
    mount -t ntfs-3g "UUID=$1" "/home/$2"
}

edit: the suggested post does not meet my requirements as its not finding the hard drive based on UUID and it is to do with USB attached storage devices

Criggie
  • 671
  • 5
  • 12
Jono
  • 505
  • 3
  • 7
  • 19
  • 1
    Does this answer your question? [Different behaviour of bash script in udev](https://askubuntu.com/questions/1417539/different-behaviour-of-bash-script-in-udev) – Raffa Jul 11 '22 at 09:47
  • I know that the title of the question I linked to above is a bit misleading ... but, what you want is in the second part of the accepted answer to that question :-) – Raffa Jul 11 '22 at 09:50
  • @Raffai looked at that answer and its confusing to understand and seems like a completely different approach to getting the manufactur name etc. – Jono Jul 11 '22 at 10:30
  • Why not just add labels to every partition and mount those? `lsblk -o name,fstype,size,fsused,label,partlabel,mountpoint` I have my data on different drives. Main drive is just data, copies are in m2_data (my m2 sata drive), data_nvme (my nmve drive), data256a (one of my flash drives). – oldfred Jul 11 '22 at 15:08
  • Can you give a short explanation of your end-goal here? Do you have a lot of drives with unknown models/filesystems? I'm not sure if automount or filesystem labels would match your needs (ie, "UUID=blah" lines in /etc/fstab) Trying to avoid a XY answer. – Criggie Jul 11 '22 at 21:52

2 Answers2

5

Background

What you, indeed, want is the VENDOR,MODEL output formatting options but, with a twist(see workaround section below):

 lsblk -o FSTYPE,UUID,VENDOR,MODEL

You might as well want to print a list and stop printing headings(for better output parsing) by adding the options -ln and exclude loop devices by adding the option -e 7 ... See cat /proc/devices under Block devices: for other device types you might want to exclude and separate them by commas like -e 7,11,....

See more options with lsblk -h:

$ lsblk -h

Usage:
 lsblk [options] [<device> ...]

List information about block devices.

Options:
 -D, --discard        print discard capabilities
 -E, --dedup <column> de-duplicate output by <column>
 -I, --include <list> show only devices with specified major numbers
 -J, --json           use JSON output format
 -O, --output-all     output all columns
 -P, --pairs          use key="value" output format
 -S, --scsi           output info about SCSI devices
 -T, --tree[=<column>] use tree format output
 -a, --all            print all devices
 -b, --bytes          print SIZE in bytes rather than in human readable format
 -d, --nodeps         don't print slaves or holders
 -e, --exclude <list> exclude devices by major number (default: RAM disks)
 -f, --fs             output info about filesystems
 -i, --ascii          use ascii characters only
 -l, --list           use list format output
 -M, --merge          group parents of sub-trees (usable for RAIDs, Multi-path)
 -m, --perms          output info about permissions
 -n, --noheadings     don't print headings
 -o, --output <list>  output columns
 -p, --paths          print complete device path
 -r, --raw            use raw output format
 -s, --inverse        inverse dependencies
 -t, --topology       output info about topology
 -w, --width <num>    specifies output width as number of characters
 -x, --sort <column>  sort output by <column>
 -z, --zoned          print zone model
     --sysroot <dir>  use specified directory as system root

 -h, --help           display this help
 -V, --version        display version

Available output columns:
         NAME  device name
        KNAME  internal kernel device name
         PATH  path to the device node
      MAJ:MIN  major:minor device number
      FSAVAIL  filesystem size available
       FSSIZE  filesystem size
       FSTYPE  filesystem type
       FSUSED  filesystem size used
       FSUSE%  filesystem use percentage
      FSROOTS  mounted filesystem roots
        FSVER  filesystem version
   MOUNTPOINT  where the device is mounted
  MOUNTPOINTS  all locations where device is mounted
        LABEL  filesystem LABEL
         UUID  filesystem UUID
       PTUUID  partition table identifier (usually UUID)
       PTTYPE  partition table type
     PARTTYPE  partition type code or UUID
 PARTTYPENAME  partition type name
    PARTLABEL  partition LABEL
     PARTUUID  partition UUID
    PARTFLAGS  partition flags
           RA  read-ahead of the device
           RO  read-only device
           RM  removable device
      HOTPLUG  removable or hotplug device (usb, pcmcia, ...)
        MODEL  device identifier
       SERIAL  disk serial number
         SIZE  size of the device
        STATE  state of the device
        OWNER  user name
        GROUP  group name
         MODE  device node permissions
    ALIGNMENT  alignment offset
       MIN-IO  minimum I/O size
       OPT-IO  optimal I/O size
      PHY-SEC  physical sector size
      LOG-SEC  logical sector size
         ROTA  rotational device
        SCHED  I/O scheduler name
      RQ-SIZE  request queue size
         TYPE  device type
     DISC-ALN  discard alignment offset
    DISC-GRAN  discard granularity
     DISC-MAX  discard max bytes
    DISC-ZERO  discard zeroes data
        WSAME  write same max bytes
          WWN  unique storage identifier
         RAND  adds randomness
       PKNAME  internal parent kernel device name
         HCTL  Host:Channel:Target:Lun for SCSI
         TRAN  device transport type
   SUBSYSTEMS  de-duplicated chain of subsystems
          REV  device revision
       VENDOR  device vendor
        ZONED  zone model
          DAX  dax-capable device

For more details see lsblk(8).

Workaround

Notice that lsblk will list VENDOR and MODEL for disks(i.e. block devices) and not partitions ... but you can get around that by implementing something like this(just a template ... modify it to your needs):

#!/bin/bash

for f in $(/bin/lsblk -nlo TYPE,NAME | /bin/awk '/part/ {print $2}') # Get partitions only
    do
    f="/dev/$f" # Set full path to partition
    uuid=$(/bin/lsblk -nlo PARTUUID "$f") # Get partition UUID
    fstype=$(/bin/lsblk -nlo FSTYPE "$f") # Get partition file system type
    d=$(/bin/lsblk -nldo PKNAME "$f") # Get partition block device
    d="/dev/$d" # Set full path to block device
    vendor=$(/bin/lsblk -nlo VENDOR "$d") # Get vendor of the block device
    model=$(/bin/lsblk -nlo MODEL "$d") # Get model of the block device
    echo "$f: PARTUUID=$uuid FSTYPE=$fstype VENDOR=$vendor MODEL=$model" # Print partition name, partition uuid, partition file system type, vendor, model 
    done

Or straight forward(by passing the partition PARTUUID(NOT DISK UUID) to a function):

model_by_partuuid () {

    p=$(/bin/readlink -f /dev/disk/by-partuuid/"$1") # Get partition device /path/name by its PARTUUID
    b=$(/bin/lsblk -nldo PKNAME "$p") # Get partition block device
    vendor=$(/bin/lsblk -nlo VENDOR /dev/"$b") # Get vendor of the block device
    model=$(/bin/lsblk -nlo MODEL /dev/"$b") # Get model of the block device
    echo "VENDOR=$vendor MODEL=$model" # Print partition vendor, model 

}

Use this function like so:

model_by_partuuid "partition-uuid-here"

So, your intended script might look something like this:

#/bin/bash

ext4_main_mountpoint="/mnt/ext4/" # Specify EXT4 filesystems main mount point
ntfs_main_mountpoint="/mnt/ntfs/" # Specify NTFS filesystems main mount point

# "echo" in-front of "mkdir" and "mount" is for dry-run(simulation) ... You need to remove "echo" once you're done testing for the script to actually create mount points and mount partitions. 

for f in $(/bin/lsblk -nlo TYPE,NAME | /bin/awk '/part/ {print $2}') # Get partitions only
    do
    f="/dev/$f" # Set full path to partition
    uuid=$(/bin/lsblk -nlo PARTUUID "$f") # Get partition UUID
    uuidf4="${uuid:0:4}" # Get the first four characters of the partition UUID(for reference ... not used below)
    uuidl4="${uuid: -4}" # Get the last four characters of the partition UUID(for reference ... not used below)
    fstype=$(/bin/lsblk -nlo FSTYPE "$f") # Get partition file system type
    d=$(/bin/lsblk -nldo PKNAME "$f") # Get partition block device
    d="/dev/$d" # Set full path to block device
    vendor=$(/bin/lsblk -nlo VENDOR "$d") # Get vendor of the block device
    vendor=$(/bin/echo "$vendor" | /bin/tr -d '[:space:]') # Trim whitespace
    model=$(/bin/lsblk -nlo MODEL "$d") # Get model of the block device
    model=$(/bin/echo "$model" | /bin/tr -d '[:space:]') # Trim whitespace
    if [ "$fstype" == "ext4" ]; then # Check if the filesystem on the partition is EXT4
        echo mkdir -p "$ext4_main_mountpoint$vendor-$model-$uuid" # Create the mount point if it doesn't exist
        echo mount -U "$uuid" "$ext4_main_mountpoint$vendor-$model-$uuid" # Mount the partition
    elif [ "$fstype" == "ntfs" ]; then # Check if the filesystem on the partition is NTFS
        echo mkdir -p "$ntfs_main_mountpoint$vendor-$model-$uuid" # Create the mount point if it doesn't exist
        echo mount -t "$fstype" -U "$uuid" "$ntfs_main_mountpoint$vendor-$model-$uuid" # Mount the partition
    else  
        echo "No rule specified for $f: PARTUUID=$uuid FSTYPE=$fstype VENDOR=$vendor MODEL=$model" # For other filesystem types(not specified above), print partition name, partition uuid, partition file system type, vendor, model
     fi 
    done

Alternatives

Furthermore, alternative methods of identifying your disks partitions and mounting them automatically to a certain mount point are described in this answer.

Raffa
  • 24,905
  • 3
  • 35
  • 79
  • Thel model and vendor do not appear on each row/partition it finds on hard disks. As i said on my original post, this does not work and have explained why in my original post – Jono Jul 11 '22 at 14:01
  • @Jono I updated the answer for that. – Raffa Jul 11 '22 at 15:14
  • Thanks i wil give that a go – Jono Jul 11 '22 at 15:22
  • I did +1 a while ago, @Raffa; `"${f::-1}"` is nice and quick, but not waterproof: I think it fails for partition numbers 10 and higher, and also for nvme and mmcblk drives. But it works for typical SATA drives and USB drives `sdxn` – sudodus Jul 11 '22 at 19:42
  • 1
    @sudodus It's supposed to be just a template ... But, you are right ... What seems clear and simple to an experienced user, might not be so simple for other users ... I will update the answer in a few minutes to meet your strict high standards :-) ... Good morning by the way. – Raffa Jul 12 '22 at 05:16
  • 1
    @sudodus I updated the template to work on all partitions on any disk type ... Please, check and let me know what you think. – Raffa Jul 12 '22 at 05:27
  • 1
    Yes @Raffa, I did a quick test of your function `model_by_partuuid`, and it works also for my nvme drive (I did not test it extensively, but I think it is waterproof now) :-) – sudodus Jul 12 '22 at 11:32
  • O you have updated this since i last saw. i will give this a go . thanks for the detailed response – Jono Jul 13 '22 at 18:09
3

I have done something similar before, modified it and thought that the following shellscript can do what you want, or at least similar to what you want. It should be possible to modify and include in your scripts.

#!/bin/bash

inversvid="\0033[7m"
resetvid="\0033[0m"

function lister {

sdx=$(<<< "$1" sed 's/[0-9]*$//')
#echo "sdx=$sdx"
nvmex=$(<<< "$sdx" grep -o '.*[0-9]')
#echo "nvmex=$nvmex"
if [ "$nvmex" != "" ]
then
 drive="$nvmex"
else
 drive="$sdx"
fi
#echo "drive=$drive"

model=$(lsblk -ndo model "$drive"| sed 's/ *$//')
partition=$(lsblk -no name,uuid "$1")

len=$(( (${#model} + ${#partition} - 13)/2 ))
#echo len=$len
leng=""
for ((i=0;i<len;i++)); do leng="${leng} ";done
echo "name${leng}uuid${leng}model"
echo -e "$partition ${inversvid}$model$resetvid"
}
########################################################################

# main

########################################################################

if [ $# -eq 1 ]
then
 echo "checking for specific UUID"
else
 echo "scanning for all UUIDs"
fi

for name in $(lsblk -nlo name)
do
 type=$(lsblk -nlo type "/dev/$name")
 if [ "$type" == "part" ]
 then
  uuid=$(lsblk -nlo uuid "/dev/$name")
  if [ $# -eq 1 ] && [ "$uuid" == "$1" ]
  then 
   lister "/dev/$name"
   break
  elif [ $# -ne 1 ] && [ "$uuid" != "" ]
  then
   lister "/dev/$name"
  fi
 fi
done

Example:

$ ./uuid2model 8ce17a86-5b31-45cc-a89d-338f618eebad
checking for specific UUID
name                           uuid                           model
nvme0n1p1 8ce17a86-5b31-45cc-a89d-338f618eebad KINGSTON SA2000M8250G

Without parameter it prints the drive models for all UUIDs found.

sudodus
  • 45,126
  • 5
  • 87
  • 151