1

I'm writing a bash script and struggling to find a way to create an associative array that holds a key and one attribute to each mounted external hard drive plugged in via USB. My example used here is an associative array, but it does not have to be. Though I feel it makes the most sense. And yes I am using bash 4 for the associative array support.

> /bin/bash --version
GNU bash, version 4.4.12(1)-release (x86_64-apple-darwin16.3.0)

Currently I'm playing around with diskutil list and system_profiler SPUSBDataType as subshells in a for loop. Or just parsing a bunch of the output through pipes with a mixture of sed, grep and awk commands. So far no avail. It should be noted that these apple commands output other things I want to discard such as bluetooth devices and my local hard drive. See below for details.

My desired array would look something like this: myArray=([Name1]=/dev/disk3s2 [Name2]=/dev/disk4s2). I really just need the name and it's mount point. The format of it is flexible. I can parse it later if in a different format. I just need to be able to do a simple for i in "!${myArray[@]}; do echo "Name = ${i}"; echo "mount point = ${myArray[$i]}"; done or something similar.

Note: at the end of the day, its will prompt the user something like "You have N number of external hard drives mounted, please select which one you would like to perform the back up to"

As requested here is example output of the main two commands (diskutil list and system_profiler SPUSBDataType) I have been using in order to try and achieve my goal. Though it should be noted, I don't care if there are other ways to achieve the same desired goal. If you know of other useful commands, please suggest.

> diskutil list
/dev/disk0 (internal, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *500.3 GB   disk0
   1:                        EFI EFI                     209.7 MB   disk0s1
   2:          Apple_CoreStorage Macintosh HD            499.4 GB   disk0s2
   3:                 Apple_Boot Recovery HD             650.0 MB   disk0s3

/dev/disk1 (internal, virtual):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:                  Apple_HFS Macintosh HD           +499.1 GB   disk1
                                 Logical Volume on disk0s2
                                 A0B29294-0031-40CB-8894-6F56545CCF3C
                                 Unencrypted

/dev/disk3 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *2.0 TB     disk3
   1:                        EFI EFI                     209.7 MB   disk3s1
   2:                  Apple_HFS Storage                 2.0 TB     disk3s2

and

> system_profiler SPUSBDataType
USB:

    USB 3.0 Bus:

      Host Controller Driver: AppleUSBXHCIWPT
      PCI Device ID: 0x9cb1
      PCI Revision ID: 0x0003
      PCI Vendor ID: 0x8086

        BUP Slim SL:

          Product ID: 0xab26
          Vendor ID: 0x0bc2  (Seagate LLC)
          Version: 1.08
          Serial Number: NA96KBJB
          Speed: Up to 5 Gb/sec
          Manufacturer: Seagate
          Location ID: 0x14500000 / 3
          Current Available (mA): 900
          Current Required (mA): 896
          Extra Operating Current (mA): 396
          Media:
            BUP Slim SL:
              Capacity: 2 TB (2,000,398,933,504 bytes)
              Removable Media: No
              BSD Name: disk3
              Logical Unit: 0
              Partition Map Type: GPT (GUID Partition Table)
              USB Interface: 0
              Volumes:
                EFI:
                  Capacity: 209.7 MB (209,715,200 bytes)
                  File System: MS-DOS FAT32
                  BSD Name: disk3s1
                  Content: EFI
                  Volume UUID: 0E239BC6-F960-3107-89CF-1C97F78BB46B
                Storage:
                  Capacity: 2 TB (2,000,054,960,128 bytes)
                  Available: 1.3 TB (1,298,787,659,776 bytes)
                  Writable: Yes
                  File System: Journaled HFS+
                  BSD Name: disk3s2
                  Mount Point: /Volumes/Storage
                  Content: Apple_HFS
                  Volume UUID: E9786DAE-78A7-31B7-B939-8FCCE3C147AB

        Bluetooth USB Host Controller:

          Product ID: 0x8290
          Vendor ID: 0x05ac  (Apple Inc.)
          Version: 1.46
          Speed: Up to 12 Mb/sec
          Manufacturer: Broadcom Corp.
          Location ID: 0x14300000 / 2
          Current Available (mA): 500
          Current Required (mA): 0
          Extra Operating Current (mA): 0
          Built-In: Yes

diskutil list | tail -n +15 will give me basically everything after my local hard drive.

Another thought I had was I could do some sort of for usb in $(system_profiler SPUSBDataType) statement. But I can't figure out the right subshell command to pipe to sed,grep,awk and other tools to parse by blank lines or indentation. More than likely I will not be able to cleanly implement it in one swipe like that.

  • Can you add an example output of those commands to the question? – Philippos Sep 25 '17 at 11:53
  • 1
    You have upgraded your system's bash to support associative arrays right? (macOS comes with bash v3~) – Jesse_b Sep 25 '17 at 11:53
  • You say simple array (e.g. indexed) but then give an example of an associative array. Jesse makes a good point about older bash versions not having associative arrays. – Jeff Schaller Sep 25 '17 at 12:51
  • @Philippos I added it to the question. I hope that helps. – Byron Mansfield Sep 27 '17 at 1:51
  • @Jesse_b I did. I also added this point to the question – Byron Mansfield Sep 27 '17 at 1:51
1

I'm not sure if this will work for you or not as it is checking only for external drives and not necessarily USB drives, but I believe it does mostly what you need:

#! /usr/local/bin/bash -
IFS= mapfile -t DISKUTIL_LIST < <(diskutil list external)
declare -A myAssocArray
for line in "${DISKUTIL_LIST[@]}"; do
    DISK_MOUNT=$(echo $line | awk '{print $1}')
    if [[ "${#DISK_MOUNT}" -lt '3' ]]; then
        :
    else
        ARRAY_INDEX=$(mount | grep "$DISK_MOUNT" | awk '{print $3}' | awk -F'/' '{print $NF}')
        MOUNT_POINT=$(mount | grep "$DISK_MOUNT" | awk '{print $1}')
        myAssocArray+=([${ARRAY_INDEX}]="$MOUNT_POINT")
    fi
done
for disk in "${!myAssocArray[@]}"; do
    echo -e "Name:\t\t${disk}\nMount Point:\t${myAssocArray[$disk]}"
done

Sample output for me (I only have one external at the moment):

{0} ➔ ./usb-drives.sh
Name:           JBUTRYN
Mount Point:    /dev/disk2s1
  • This looks like it may work. I did not realize you could do diskutil list external. That is a huge help. I will try it as soon as I have some external disks to test with and let you know. I am traveling at the moment and I'm in a lost luggage situation -_- But if you could show me an example of the output that would be helpful. – Byron Mansfield Sep 27 '17 at 2:01
  • @ByronMansfield . I've updated the original script to produce the type of output you wanted, and included a sample output. – Jesse_b Sep 27 '17 at 15:16
  • Awesome! I think this is exactly what I'm looking for. Give me some time to try this myself and I'll mark it is answered. Thank you. – Byron Mansfield Sep 28 '17 at 7:25
  • Confirmed. This is what I was looking for. Thank you for teaching me something new and the ability to use external as an extra argument to diskutil list. – Byron Mansfield Sep 29 '17 at 2:00

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.