How can I know if /dev/sdX is a local HDD or USB key?
I’d prefer a way of doing this without root privileges.
OK, udevadm helped a lot:
For local HDD:
udevadm info --query=all --name=sdb | grep ID_BUS E: ID_BUS=ata
For USB key:
udevadm info --query=all --name=sdc | grep ID_BUS E: ID_BUS=usb
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
There are a few ways to tell without root privileges, many of them tricky/hacky:
Using /dev/disk/by-id:
find /dev/disk/by-id/ -lname '*sdX'
If this responds with something like /dev/disk/by-id/usb-blah-blah-blah, then it’s a USB disk. Other prefixes include ata, dm, memstick, scsi, etc.
Using /dev/disk/by-path isn’t significantly different:
find /dev/disk/by-path/ -lname '*sdX'
You’ll get something like /dev/disk/by-path/pci-0000:00:1d.7-usb-0:1:1.0-scsi-0:0:0:0. This shows the device path leading to the disk. In this case, a rough path is PCI → USB → disk. (note the -usb-).
Using udev (I run Debian. My udevadm is in /sbin which isn’t on my $PATH — yours might be elsewhere, on or off your $PATH):
/sbin/udevadm info --query=all --name=sdX | grep ID_BUS
You’ll get the bus type the device is on. Remove the | grep ID_BUS for the complete listing of information (you may need to add |less).
If you have lshw installed, Huygens’ answer may also work:
lshw -class disk -class storage | less
And look through the output for your disk. In less, try / sdX and look at the preceding, bus info lines — the first one will just say [email protected]…, but the one several lines before it will be more enlightening. However, you really should run this as the superuser so it may not be suitable. (symptoms: on the laptop I tried it, it listed the SATA disk but not the USB one — running with sudo listed both)
There are other ones too, more or less direct than these ones.
Method 2
You could use lsblk to report TRAN (device transport type)
:
lsblk -do name,tran
NAME TRAN sda sata sdb sata sdd usb
where -dor --nodeps means don’t print slaves and -o name,tran or --output name,tran means list only name of device and device transport type. Add rm to the list of output columns to see which devices are removable (1 if true):
lsblk --nodeps --output NAME,TRAN,RM
NAME TRAN RM sda sata 0 sdb sata 0 sdd usb 1
or -n to remove headers, e.g. to print only the transport type for a certain drive:
lsblk -ndo tran /dev/sdb
sata
Note that modern versions of lsblk (2.27 and newer) support JSON output so you could also do something like:
lsblk -Jdo name,tran | jq -r '.blockdevices[] | select(.tran=="usb") | .name'
to list only block devices connected on the USB bus.
Method 3
I know a solution, but, sadly, it requires root privilege.
Anyway, you might still find it useful:
sudo lshw -class disk -class storage
For each device it will print the logical name (e.g., /dev/sda) and bus info, which in case of a USB device would be something like ‘[email protected]:2’.
Sample output:
[...]
*-storage
description: SATA controller
physical id: d
bus info: <a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="82f2e1ebc2b2b2b2b2">[email protected]</a>:00:0d.0
configuration: driver=ahci latency=64
[...]
*-disk:0
description: ATA Disk
physical id: 0
bus info: <a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="88fbebfbe1c8ba">[email protected]</a>:0.0.0
logical name: /dev/sda
[...]
*-scsi
physical id: 3
bus info: <a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="f1848293b1c0">[email protected]</a>:2
configuration: driver=usb-storage
*-disk
description: SCSI Disk
physical id: 0.0.0
bus info: <a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="bbc8d8c8d2fb8d">[email protected]</a>:0.0.0
logical name: /dev/sdc
[...]
Method 4
This doesn’t need root privileges
(but many of these commands use and depend on bashisms,
so they will not work in all POSIX-compliant shells):
There is a quick way to ask about a sdX:
grep -H . /sys/block/sda/{capability,uevent,removable,device/{model,type,vendor,uevent}}
/sys/block/sda/capability:52
/sys/block/sda/uevent:MAJOR=8
/sys/block/sda/uevent:MINOR=0
/sys/block/sda/uevent:DEVNAME=sda
/sys/block/sda/uevent:DEVTYPE=disk
/sys/block/sda/removable:0
/sys/block/sda/device/model:WDC WD360GD-00FN
/sys/block/sda/device/type:0
/sys/block/sda/device/vendor:ATA
/sys/block/sda/device/uevent:DEVTYPE=scsi_device
/sys/block/sda/device/uevent:DRIVER=sd
/sys/block/sda/device/uevent:MODALIAS=scsi:t-0x00
The really interesting file is capability. On my Debian, I have a genhd.h file, so:
eval $(sed -ne '
s/#define.*GENHD_FL_([A-Z0-9_]*)[ t]*([0-9]*) ?.*$/GENHD_FLAGS[2]="1"/p
' /usr/src/linux-headers-2.6.32-5-common-openvz/include/linux/genhd.h)
diskCapa=$(</sys/block/sda/capability)
for i in ${!GENHD_FLAGS[@]};do
(( diskCapa & i )) && echo ${GENHD_FLAGS[i]}
done
MEDIA_CHANGE_NOTIFY
UP
SUPPRESS_PARTITION_INFO
diskCapa=$(</sys/block/sdd/capability)
for i in ${!GENHD_FLAGS[@]};do
(( diskCapa & i )) && echo ${GENHD_FLAGS[i]}
done
REMOVABLE
MEDIA_CHANGE_NOTIFY
UP
SUPPRESS_PARTITION_INFO
At all, for only knowing if flag removable is set:
grep REMOVABL /usr/src/linux-headers-3.2.0-4-common/include/linux/genhd.h #define GENHD_FL_REMOVABLE 1
so
for disk in sd{a,b,c,d,e,f,g,h} ; do
(( $(< /sys/block/$disk/capability ) & 1 )) && echo $disk is removable
done
works by testing whether the capability value
(which is 52 in my sda example, above)
has the 1 bit set (i.e., whether it is an odd number).
But Linux renders all flags in /sys, so asking for /sys/block/sdX/removable is a lot simpler! 😉
So a USB key could be removable, but as there are lots of removable devices, I would prefer to ensure that the size of the medium is greater than 0 (like an unloaded CD-ROM tray, for sample)
and that the device is not in use: In watching that sdX/trace/enable is not binded:
Nota: All this is well tested on bash v4.2+.
Under bash, you could use this very quick and efficient way:
for disk in /sys/block/* ; do
[ -f "$disk/removable" ] && [ $(<"$disk/removable") -gt 0 ] &&
[ -f "$disk/size" ] && [ $(<"$disk/size") -gt 0 ] &&
[ -f "$disk/trace/enable" ] && [ -z "$(<"$disk/trace/enable")" ] &&
echo "${disk##*/} $(($(<"$disk/size")/1953125))G $(<"$disk/device/model")"
done
On my system, there are 4 USB keys, but one of them (sde) is already mounted, so the previous command output:
sdd 8G Trans-It Drive
sdf 7G Storage Media
sdg 4G silicon-power
My script:
There is a little bash function I wrote to install upgraded Debian Live.
#!/bin/bash
txtsize() {
local _c=$1 _i=0 _a=(b K M G T P)
while [ ${#_c} -gt 3 ] ; do
((_i++))
_c=$((_c>>10))
done
_c=000$(( ( $1*1000 ) >> ( 10*_i ) ))
((_i+=${3:-0}))
printf -v ${2:-REPLY} "%.2f%s" ${_c:0:${#_c}-3}.${_c:${#_c}-3} ${_a[_i]}
}
# The first part only renders human readable size. The function begins there.
chooseFreeUsbKey() {
local _lUdisk _lUsize _lUdialog=dialog # whiptail # gdialog
local -A _lUdevices
unset ${1:-REPLY}
for _lUdisk in /sys/block/*; do
[ -f $_lUdisk/removable ] && [ $(<$_lUdisk/removable) -gt 0 ] &&
[ -f $_lUdisk/size ] && [ $(<$_lUdisk/size) -gt 0 ] &&
txtsize $(<$_lUdisk/size)*512 _lUsize &&
[ -f $_lUdisk/trace/enable ] && [ -z "$(<$_lUdisk/trace/enable)" ] &&
_lUdevices[${_lUdisk##*/}]="$_lUsize $(<$_lUdisk/device/model)"
done
case ${#_lUdevices[@]} in
0 ) ;; # echo Sorry no key found. ;;
1 ) IFS=§ read -a ${1:-REPLY}
<<< "${!_lUdevices[@]}§${_lUdevices[@]%% *}§${_lUdevices[@]#* }";;
* ) declare -a menu
for _lUdisk in ${!_lUdevices[@]}; do
menu+=($_lUdisk "${_lUdevices[$_lUdisk]}")
done
_lUdisk=$($_lUdialog --menu "Choose a USB stick"
$((LINES-3)) $((COLUMNS-3)) $((LINES-8))
"${menu[@]}" 2>&1 >/dev/tty)
IFS=§ read -a ${1:-REPLY}
<<< "$_lUdisk§${_lUdevices[$_lUdisk]%% *}§${_lUdevices[$_lUdisk]#* }"
esac
}
This assigns the answer, as an array, to the variable given as the first argument or to variable $REPLY:
chooseFreeUsbKey stick
echo "$stick"
sdf
echo "${stick[1]}"
7.26G
echo "${stick[2]}"
Storage Media
(The last field may contain spaces.)
Method 5
I suggest just using hdparm or lshw (which you might need to install), and using sudo to execute it as root.
sudo hdparm -I /dev/sda sudo lshw -short -C disk sudo lshw -class disk -class storage
should all give you information.
Method 6
Just read value of /sys/block/sdX/removable.
For example:
$ cat /sys/block/sda/removable 0 $ cat /sys/block/sdc/removable 1
/dev/sdc is an USB key (it could be a SD card or any other removable media).
Method 7
dmesg is the easiest method:
dmesg | grep sdX
(sdX being the name of your device, e.g., sda)
From the command above, you will see the following:
- Attached SCSI disk (hard disk)
- Attached SCSI removable disk (removable media; such as, USB flash drive)
Method 8
You can use the below commands to get SD, USB, and SATA device nodes.
usb_device="/dev/`ls -lR /dev/disk/by-id/ | grep ^l | grep 'usb' | awk '{print $NF}' | cut -d '/' -f 3 | awk 'NR == 1'`"
sata_device="/dev/`ls -lR /dev/disk/by-id/ | grep ^l | grep 'ata' | awk '{print $NF}' | cut -d '/' -f 3 | awk 'NR == 1'`"
sd_device="/dev/`ls -lR /dev/disk/by-id/ | grep ^l | grep 'mmc' | awk '{print $NF}' | cut -d '/' -f 3 | awk 'NR == 1'`"
Method 9
I’m lazy, inxi tells me this easily:
inxi -D
Drives: HDD Total Size: 1220.3GB (33.2% used)
ID-1: /dev/sda model: ST380817AS size: 80.0GB
ID-2: /dev/sdb model: WDC_WD1003FZEX size: 1000.2GB
ID-3: USB /dev/sdc model: USB_Flash_Drive size: 140.0GB
I believe it also tells me if it’s firewire and maybe one other type, but I’d have to double check, haven’t used those types in a while.
It also tells me using -p if partitions are remote, like samba or nfs mounts.
Method 10
On Linux, you can get the complete path of any device from sysfs. No privileges needed.
For each block device there is symlink of the form major:minor (in decimal) inside /sys/dev/block pointing to the complete path of the device through all the buses. Same for character devices inside /sys/dev/char. Here is an example that should also work on devices without bash, stat, util-linux, udev, lshw, hdparm, sudo, perl/python, jq, golang, etc:
syspath(){ readlink -f /sys/dev/$(ls -l "$1" | awk -F'[, ]+' '{print ($1~/^c/?"char/":"block/")$5":"$6}'); }
syspath /dev/sda
/sys/devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb2/2-1/2-1:1.0/host0/target0:0:0/0:0:0:0/block/sda
^^^^
Method 11
After you plug in the USB device, run dmesg in a console window. You will be provided with some hints.
For example it will says something along the lines of “Device plugged in, mass storage /dev/sdd”.
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0