BASH Script to Detect and List USB Flash Drives

Hello. This is my first post to this forum. I've read many of the posts over the last two or three years and I've learned a lot.

I'm creating a live Linux distribution using the Linux Live Scripts -- just as a hobby project -- and I'm wanting to create an automated way for a user to copy the folders on the boot CD to a connected flash drive. The interface for that function should be a menu listing of the drive or drives. It should display:

  • the manufacturer (i.e., Kingston)
  • the model (i.e., Data Traveler)
  • the capacity in the format "512MB" or "1GB" or "4GB"
  • the path (i.e. /mnt/sda1)

The user would select the drive and the copy would begin.

I've got a ton of books and I'm pounding topics like awk and grep into my head, but I'm almost 50 and not as bright as I used to be :). I could probably do it, but I'm about about three months out if I insist on figuring it out on my own. And although that might build character, if someone has already created such a script I'd really rather just grab that code and modify it.

LSHW seems like an obvious way to get the device information, but I'm good with any method. Has anyone out there done such a BASH script already? My Google searches have failed to find one. Thanks in advance for any thoughts or suggestions.

Godz

It's not quite a simple script because it involves tying several sources of information together. The point of the UNIX device model is to make it irrelevant whether a device is USB or IDE or SCSI or whatever from a programmer's point of view, getting all the info you need would mean tying together info from /sys/, lsusb, and possibly udev as well... Does linux live have udev?

Here is the start of something combining info from /sys/ with udev. From here we see that not all manufacturers encode their USB devices with any sort of sane, or even identifiable code values... This readout is from a generic MP3 player device with an occupied card-slot, the 512M of built-in storage is sdb and the card slot is sdc.

#!/bin/bash

for DEV in /sys/block/sd*
do

	if readlink $DEV/device | grep -q usb
	then
		DEV=`basename $DEV`
		echo "$DEV is a USB device, info:"
		udevinfo --query=all --name $DEV
		if [ -d /sys/block/${DEV}/${DEV}1 ]
		then
			echo "Has partitions " /sys/block/$DEV/$DEV[0-9]*
		else
			echo "Has no partitions"
		fi
		echo
	fi
done

exit 0
$ ./getusb.sh

sdb is a USB device, info:
P: /block/sdb
N: sdb
S: disk/by-id/usb-SMI_USB_DISK_AA04012700498000-0:0
S: disk/by-path/pci-0000:00:0b.1-usb-0:1:1.0-scsi-0:0:0:0
S: disk/by-uuid/F4E6-8AEC
E: DEVTYPE=disk
E: ID_VENDOR=SMI
E: ID_MODEL=USB_DISK
E: ID_REVISION=4000
E: ID_SERIAL=SMI_USB_DISK_AA04012700498000-0:0
E: ID_SERIAL_SHORT=AA04012700498000
E: ID_TYPE=disk
E: ID_INSTANCE=0:0
E: ID_BUS=usb
E: ID_PATH=pci-0000:00:0b.1-usb-0:1:1.0-scsi-0:0:0:0
E: ID_FS_USAGE=filesystem
E: ID_FS_TYPE=vfat
E: ID_FS_VERSION=FAT16
E: ID_FS_UUID=F4E6-8AEC
E: ID_FS_UUID_ENC=F4E6-8AEC
E: ID_FS_LABEL=
E: ID_FS_LABEL_ENC=
E: ID_FS_LABEL_SAFE=
Has no partitions

sdc is a USB device, info:
P: /block/sdc
N: sdc
S: disk/by-id/usb-SMI_USB_DISK_AA04012700498000-0:1
S: disk/by-path/pci-0000:00:0b.1-usb-0:1:1.0-scsi-0:0:0:1
E: DEVTYPE=disk
E: ID_VENDOR=SMI
E: ID_MODEL=USB_DISK
E: ID_REVISION=4000
E: ID_SERIAL=SMI_USB_DISK_AA04012700498000-0:1
E: ID_SERIAL_SHORT=AA04012700498000
E: ID_TYPE=disk
E: ID_INSTANCE=0:1
E: ID_BUS=usb
E: ID_PATH=pci-0000:00:0b.1-usb-0:1:1.0-scsi-0:0:0:1
Has partitions  /sys/block/sdc/sdc1
$

Thanks, Corona, for those replies; however, I believe that the LSHW utility will do nicely for gathering the information I'm wanting to list for the users. Here is a sample output from my machine. Two flash drives are connected (one of them older and painfully small -- 64MB -- the other 1GB).

This is the result of the "lshw -short" command.

H/W path                Device     Class       Description
==========================================================
                                   system      PROD00000000
/0                                 bus         Motherboard
/0/0                               processor   Intel(R) Pentium(R) 4 CPU 2.00GHz
/0/0/0                             memory      8KiB L1 cache
/0/0/1                             memory      512KiB L2 cache
/0/1                               memory      2021MiB System memory
/0/100                             bridge      651 Host
/0/100/1                           bridge      Virtual PCI-to-PCI bridge (AGP)
/0/100/1/0                         display     NV44A [GeForce 6200]
/0/100/2                           bridge      SiS962 [MuTIOL Media IO]
/0/100/2.1                         bus         SiS961/2 SMBus Controller
/0/100/2.5                         storage     5513 [IDE]
/0/100/2.5/0            ide0       bus         IDE Channel 0
/0/100/2.5/0/0          /dev/hda   disk        160GB SAMSUNG SP1604N
/0/100/2.5/0/0/1        /dev/hda1  volume      127GiB Windows NTFS volume
/0/100/2.5/0/0/2        /dev/hda2  volume      21GiB Windows NTFS volume
/0/100/2.5/0/1          /dev/hdb   disk        81GB Maxtor 6L080L0
/0/100/2.5/0/1/1        /dev/hdb1  volume      76GiB EXT3 volume
/0/100/2.5/1            ide1       bus         IDE Channel 1
/0/100/2.5/1/0          /dev/hdc   disk        SONY DVD RW DW-Q30A
/0/100/2.5/1/0/0        /dev/hdc   disk
/0/100/2.5/1/1          /dev/hdd   disk        LITE-ON LTR-52327S
/0/100/2.7                         multimedia  AC'97 Sound Controller
/0/100/3                           bus         USB 1.1 Controller
/0/100/3/1              usb4       bus         OHCI Host Controller
/0/100/3.1                         bus         USB 1.1 Controller
/0/100/3.1/1            usb5       bus         OHCI Host Controller
/0/100/3.3                         bus         USB 2.0 Controller
/0/100/3.3/1            usb1       bus         EHCI Host Controller
/0/100/a                           bus         USB
/0/100/a/1              usb6       bus         OHCI Host Controller
/0/100/a.1                         bus         USB
/0/100/a.1/1            usb7       bus         OHCI Host Controller
/0/100/a.1/1/1                     input       Microsoft IntelliMouse
/0/100/a.2                         bus         USB 2.0
/0/100/a.2/1            usb2       bus         EHCI Host Controller
/0/100/a.2/1/3          scsi2      storage     Flash Disk
/0/100/a.2/1/3/0.0.0    /dev/sda   disk        1029MB SCSI Disk
/0/100/a.2/1/3/0.0.0/1  /dev/sda1  volume      981MiB Windows FAT volume
/0/100/b                           bus         VT82xxxxx UHCI USB 1.1 Controller
/0/100/b/1              usb8       bus         UHCI Host Controller
/0/100/b/1/1            scsi3      storage     Mass Storage Device
/0/100/b/1/1/0.0.0      /dev/sdb   disk        65MB SCSI Disk
/0/100/b/1/1/0.0.0/1    /dev/sdb1  volume      62MiB Windows FAT volume
/0/100/b.1                         bus         VT82xxxxx UHCI USB 1.1 Controller
/0/100/b.1/1            usb9       bus         UHCI Host Controller
/0/100/b.2                         bus         USB 2.0
/0/100/b.2/1            usb3       bus         EHCI Host Controller
/0/100/d                eth0       network     RTL-8139/8139C/8139C+

Only takes a couple of seconds. True, lshw tends to view flash drives as SCSI devices, but I don't see that as a dealbreaker.

Another way to do what you want is to query the devices using the hal-* utilities which are available on most modern GNU/Linux distributions.

#!/bin/ksh93

for udi in $(/usr/bin/hal-find-by-capability --capability storage)
do
    device=$(hal-get-property --udi $udi --key block.device)
    vendor=$(hal-get-property --udi $udi --key storage.vendor)
    model=$(hal-get-property --udi $udi --key storage.model)
    if [[ $(hal-get-property --udi $udi --key storage.bus) = "usb" ]]
    then
        parent_udi=$(hal-find-by-property --key block.storage_device --string $udi)
        mount=$(hal-get-property --udi $parent_udi --key volume.mount_point)
        label=$(hal-get-property --udi $parent_udi --key volume.label)
        media_size=$(hal-get-property --udi $udi --key storage.removable.media_size)
        size=$(( ceil(media_size/(1000*1000*1000)) ))
        printf "$vendor  $model  $device  $mount  $label "${size}GB" \n"
    fi
done

Here is the output for two USB thumb drives

$ ./listusb   
Kingston  DataTraveler 2.0  /dev/sdd  /media/KINGSTON  KINGSTON 1GB 
Kingston  DataTraveler 2.0  /dev/sdc  /media/USB-4GB  USB-4GB 4GB 

Thanks, pfmurphy. That's a huge help.

A system with SATA will consider those drives as SCSI as well.. in fact, on newer linux systems, it will consider IDE drives as SCSI too.

Corona: that's true, I did notice that when I was testing lshw. So fpmurphy's script does much better.