Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

How can I know if /dev/sdX is a local HDD or usb key ? best 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
share|improve this question
up vote 10 down vote accepted

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 scsi@…, 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.

share|improve this answer
Are there ways to tell apart an internal SATA drive versus an external SATA drive connected over esatap? – CMCDragonkai Nov 1 '15 at 9:07

I know a solution, but it requires 'root' privilege sadly. Anyway, you might still found it usefull:

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 USB device would be something like 'usb@1:2'.

Sample output:

       description: SATA controller
       physical id: d
       bus info: pci@0000:00:0d.0
       configuration: driver=ahci latency=64
          description: ATA Disk
          physical id: 0
          bus info: scsi@2:0.0.0
          logical name: /dev/sda
       physical id: 3
       bus info: usb@1:2
       configuration: driver=usb-storage
          description: SCSI Disk
          physical id: 0.0.0
          bus info: scsi@6:0.0.0
          logical name: /dev/sdc
share|improve this answer

Introduction [Updated]: See section about bash and bashisms

Last edit: added not in use filter.

A little of theory...

This doesn't need root privileges:

There are a quick way to ask about a sdX:

grep -H . /sys/block/sda/{capability,uevent,removable,device,{model,type,vendor,uevent}}
/sys/block/sda/device/model:WDC WD360GD-00FN

The realy 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)
for i in ${!GENHD_FLAGS[@]};do
    [ $((diskCapa & i)) -ne 0 ] && echo ${GENHD_FLAGS[i]}

    for i in ${!GENHD_FLAGS[@]};do
    [ $((diskCapa & i)) -ne 0 ] && echo ${GENHD_FLAGS[i]}

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


for disk in sd{a,b,c,d,e,f,g,h} ;do
 [ $(($(< /sys/block/$disk/capability )&1)) -ne 0 ] && echo $disk is removable

But Linux render all flags in /sys, so asking for /sys/block/sdX/removable is a lot simplier! ;-)

So an USB key could be removable, but as there are lot of removable device, I would prefer to ensure that size of medium is greater than 0 (like an unloaded cd-rom tray, for sample)

and that device is not in use: In watching that sdX/trace/enable is not binded:

bash and bashisms

Nota: All this is well tested on bash v4.2+.

Under , you could use this very quick en 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)

On my system, there is 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 function I wrote to install upgraded Debian Live.


txtsize() {
    local _c=$1 _i=0 _a=(b K M G T P)
    while [ ${#_c} -gt 3 ] ;do
    _c=000$(( ( $1*1000 ) >> ( 10*_i ) ))
    printf -v ${2:-REPLY} "%.2f%s" ${_c:0:${#_c}-3}.${_c:${#_c}-3} ${_a[_i]}

# The first part only render human readable size. The function begin 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)"
    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]}")
           _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]#* }"

This assign the answer, as an array, to variable given as 1st argument or to variable $REPLY:

chooseFreeUsbKey stick

echo $stick

echo ${stick[1]}

echo ${stick[2]}
Storage Media

(Last field may contain spaces)

share|improve this answer
Thanks to @StephaneChazelas for making the first part of my answer more readable. – F. Hauri Jan 5 '13 at 19:34

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.

share|improve this answer
hdparm on a virtual disk: hdparm -i /dev/sda /dev/sda: HDIO_DRIVE_CMD(identify) failed: Invalid exchange HDIO_GET_IDENTITY failed: Invalid argument – Tim Jun 6 '12 at 13:47
Well, I said should and it works here with virtual disks. – EightBitTony Jun 6 '12 at 13:52

Just read value of /sys/block/sdX/removable.

For example:

$ cat /sys/block/sda/removable
$ cat /sys/block/sdc/removable

/dev/sdc is an USB key (it could be a SD card or any other removable media).

share|improve this answer

dmesg is the easiest method:

dmesg | grep sd* 

(sd* 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)
share|improve this answer

You could use lsblk:

lsblk -do 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
sda  sata    0
sdb  sata    0
sdd  usb     1
share|improve this answer

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".

share|improve this answer
Please provide more information/clarification to make your answer useful. – uther Jun 6 '12 at 13:20
How about not being insulting when answering a question? – Shadur Jun 8 '12 at 11:30

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.