This works with a few different usb keys I have that I tested mounting through a variety of methods (keyboard hub, direct, external hub).
It returns a list of lists like so.
USB_Drives {{serial number 1, mount point1}, {serial number 2, mount point2}, etc.}
set USB_Drives to {}
set USB to paragraphs of (do shell script "system_profiler SPUSBDataType -detailLevel basic")
repeat with i from 1 to (count of USB)
if item i of USB contains "Removable Media: Yes" then
set {end of USB_Drives, i} to process_usb_device(i, USB)
end if
end repeat
on process_usb_device(i, USB)
set current_item to item i of USB
repeat until current_item = ""
set i to i + 1
set current_item to item i of USB
if current_item contains "Serial Number:" then
set serial_num to text ((offset of ": " in current_item) + 2) through -1 of current_item
else if current_item contains "Mount Point:" then
set mount_point to text ((offset of ": " in current_item) + 2) through -1 of current_item
end if
end repeat
return {{serial_num, mount_point}, i}
end process_usb_device
This grabs only the serial numbers of removable media
set USB_Drives to {}
set USB to paragraphs of (do shell script "system_profiler SPUSBDataType -detailLevel basic | grep 'Removable\\|Serial'")
set i to 1
repeat until i > (count USB)
if item i of USB contains "Removable Media: Yes" then
set serial to item (i + 1) of USB
set end of USB_Drives to text ((offset of ": " in serial) + 2) through -1 of serial
set i to i + 1
end if
set i to i + 1
end repeat
Thanks so much, StefanK. I couldn’t have figured that out given a millenium to do it
It gives me an event log that reads:
tell current application
do shell script “system_profiler SPUSBDataType -detailLevel basic | grep ‘Removable\|Serial’”
" USB Serial Adaptor:
Serial Number: 00022285
Serial Number: 7060IK
Removable Media: Yes
Serial Number: 0831410EBF80F12F"
offset of “: " in " Serial Number: 0831410EBF80F12F”
32
end tell
How would I quote that as a result to poke it into a FileMaker Pro global?
tell me to set field “Global” to result
.doesn’t work. What declared variable should I tell it to store?
Sorry, I don’t use FileMaker.
The result of the script is the variable USB_drives, which contains a list of strings (the serial numbers)
or an empty list, if no match is found
I am pretty sure they are different kinds of serial numbers.
“Volume Serial Number” sounds like it is part of the disk boot-record/partition-table or the filesystem itself (I vaguely remember such numbers reported in CHKDSK). Reformating the device will probably cause it to change. Googled turned up several hits, among them one that says the number is based on the date and time that the disk was formatted.
If the number reported by system_profiler is based on the hardware, then there is no reason the two serial numbers should be similar.
It only pretends to get the serial numbers and mount points of attached USB drives, whereas the OP asked for the serial numbers and names of devices.
It assumes there’s only one volume/mount point per drive.
The cue used to start parsing the text lines for the drives currently comes after the serial numbers (where they exist), so the handler never finds those lines.
It looks as if the scripter thinks that changing the value of i in the run handler repeat has some influence on the repeat index.
Parsing the System Profiler text isn’t currently as simple as it appears to have been back in 2009.
set USB_Drives to {}
set USB to paragraphs of (do shell script "system_profiler SPUSBDataType -detailLevel basic")
set i to 0
set iMax to count USB
repeat
set i to i + 1
if i > iMax then exit repeat
--if item i of USB contains "Removable Media: Yes" then
if item i of USB contains "Product ID:" then
set {maybe, i} to process_usb_device(i, USB)
if class of maybe is list then set end of USB_Drives to maybe
end if
end repeat
USB_Drives
on process_usb_device(i, USB)
set current_item to item i of USB
set serial_num to "?" # default value
set mount_point to "?" # default value
set isRemovable to false
repeat --until current_item = ""
set i to i + 1
if i > (count USB) then exit repeat
set current_item to item i of USB
log current_item
if current_item contains "Serial Number:" then
set serial_num to text ((offset of ": " in current_item) + 2) through -1 of current_item
else if current_item contains "Removable Media: Yes" then
set isRemovable to true
else if current_item contains "Mount Point:" then
set mount_point to text ((offset of ": " in current_item) + 2) through -1 of current_item
exit repeat
end if
end repeat
if isRemovable then
return {{serial_num, mount_point}, i}
else
return {false, i}
end if
end process_usb_device
One of the problems was that as designed, the original code failed to define the variables serial_num or mount_point.
In my case,it’s serial_num which wasn’t defined.
Here is a part of the contents of the variable USB:
[format]
Patriot Memory:
[/format]
As you may see, the first occurrence of the string “Removable Media: Yes” is 11 lines after the one containing the device’s Serial Number so the handler was unable to see it.
That’s a bit of an understatement Even parsing it as a property list is laborious. But this works here, at least:
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
set USB to do shell script "system_profiler -xml SPUSBDataType"
set aString to current application's NSString's stringWithString:USB
set theData to aString's dataUsingEncoding:(current application's NSUTF8StringEncoding)
set {theThing, theError} to current application's NSPropertyListSerialization's propertyListWithData:theData options:0 |format|:(missing value) |error|:(reference)
set theItems to (theThing's firstObject()'s valueForKeyPath:"_items")'s firstObject()'s valueForKeyPath:"_items._items"
set theInfo to {}
repeat with anItem in theItems
if (anItem's isKindOfClass:(current application's NSArray)) then
repeat with subItem in anItem
set sn to (subItem's valueForKey:"serial_num")
if (sn is not missing value) then
set theMedia to (subItem's valueForKey:"Media")
if (theMedia is not missing value) then
repeat with aMedium in theMedia
if aMedium is not missing value and (aMedium's valueForKey:"removable_media") as text = "yes" then
set theVolumes to (aMedium's valueForKey:"volumes")
repeat with aVolume in theVolumes
set end of theInfo to {mountPoint:((aVolume's valueForKey:"mount_point") as text), serialNo:(sn as text)}
end repeat
end if
end repeat
end if
end if
end repeat
end if
end repeat
return theInfo
With a single USB device connected, it returns a list of two lists :
[format]{{mountPoint:“missing value”, serialNo:“07981808B2F1361F”}, {mountPoint:“/Volumes/Yosemite Install Disk - 10.10.4”, serialNo:“07981808B2F1361F”}}[/format]
As you may see,the same device is returned twice:
1 - with no mountPoint
2 - with its true mountPoint
My understanding is that a complementary test upon mountPoint is required.
repeat with aMedium in theMedia
if aMedium is not missing value and (aMedium's valueForKey:"removable_media") as text = "yes" then
set theVolumes to (aMedium's valueForKey:"volumes")
repeat with aVolume in theVolumes
set maybe to (aVolume's valueForKey:"mount_point")
if maybe is not missing value then
set end of theInfo to {mountPoint:maybe as text, serialNo:(sn as text)}
end if
end repeat
end if
end repeat
In a nod to the original question in this thread, this returns (on my mid-2009 MBP running El Capitan) a list of records containing the name, serial number (where available) and mount points (where appropriate) of connected USB devices. To keep the result simple, the structure doesn’t include the buses, but it could if desired:
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
set USB to (do shell script "system_profiler -xml SPUSBDataType" without altering line endings)
-- set USB to (read (choose file) as «class utf8») -- For testing with Yvan's XML results.
set aString to current application's class "NSString"'s stringWithString:(USB)
set theData to aString's dataUsingEncoding:(current application's NSUTF8StringEncoding)
set {theThing, theError} to current application's class "NSPropertyListSerialization"'s propertyListWithData:(theData) options:(0) |format|:(missing value) |error|:(reference)
set deviceDetails to current application's class "NSMutableArray"'s new()
parseRecursively(theThing's firstObject(), deviceDetails)
return deviceDetails as list
on parseRecursively(thisThing, deviceDetails)
set subthings to thisThing's valueForKey:("_items")
repeat with thisSubthing in subthings
if (thisSubthing's allKeys()'s containsObject:("_items")) then
parseRecursively(thisSubthing, deviceDetails)
else
-- In El Capitan, 'volumes', if it exists, is a direct property of a device. In Sierra, it's apparently a property of individual 'media' which a device may have.
if (thisSubthing's allKeys()'s containsObject:("Media")) then -- Sierra.
set theMedia to (thisSubthing's valueForKey:("Media"))
set mediaDetails to current application's class "NSMutableArray"'s new()
repeat with thisMedium in theMedia
set theseDetails to (current application's class "NSDictionary"'s dictionaryWithObjects:({thisMedium's valueForKey:("_name"), thisMedium's valueForKeyPath:("volumes.mount_point")}) forKeys:({"name", "mount points"}))
tell mediaDetails to addObject:(theseDetails)
end repeat
set thisEntry to (current application's class "NSDictionary"'s dictionaryWithObjects:({thisSubthing's valueForKey:("_name"), thisSubthing's valueForKey:("serial_num"), mediaDetails}) forKeys:({"name", "serial number", "media"}))
else -- El Capitan.
set mountPoints to (thisSubthing's valueForKeyPath:("volumes.mount_point")) -- List of mount points.
set thisEntry to (current application's class "NSDictionary"'s dictionaryWithObjects:({thisSubthing's valueForKey:("_name"), thisSubthing's valueForKey:("serial_num"), mountPoints}) forKeys:({"name", "serial number", "mount points"}))
end if
tell deviceDetails to addObject:(thisEntry)
end if
end repeat
end parseRecursively
Edits: 1. Script replaced with a recursive version which burrows down through the “_items” of any objects which have them. This should remove the previous assumption about the hierarchy depth. 2. Another revision based on a couple of XML inputs which Yvan helpfully sent me. In his El Capitan XML, the ‘volumes’ are direct properties of the devices. In Sierra, they’re properties of individual ‘Media’ possessed by the devices.