Take the 2-minute tour ×
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.

Each device node under /dev has its own major/minor number pair. I know that we can retrieve this pair of numbers from the device node by means of stat, like this:

stat -c 'major: %t minor: %T' <file>

Or, ls -l also shows these numbers.

But how can we get device node(s) by given major and minor numbers? The only way I'm aware of is some kind of ls -l + awk trick, but I really hope there is better solution.

share|improve this question
    
@mikeserv, yes I know that some devices can share these numbers, so in my initial question I've mentioned: "get device node(s)". Ideally I want to get a list with all device nodes whose major/minor numbers match, one node per line. That's strange we don't have a ready tool for that. Thanks for the answer btw! –  Dmitry Frank Aug 23 at 20:21

3 Answers 3

up vote 3 down vote accepted

I found a simpler approach using the sys pseudofilesystem, at /sys/dev you have the devices ordered by type an then by major/minor, the file uevent contains the device name and a bunch of other info.

So for example,

  for file in $(find /sys/dev/ -name 7:0); do  
      source ${file}/uevent; echo $DEVNAME;
  done;

Echoes,

loop0
vcs

Note: This was tested in Debian Wheezy

share|improve this answer
    
Thanks, it seems the least-hacky way. –  Dmitry Frank Aug 24 at 8:46

Not sure what you mean.

mknod foo b 8 0

Will create the device file called foo as a block device with major 8 and minor 0. If you mean to find one or any of the files in /dev that have the same type, major and minor, you can do (with zsh):

  • For block device 8:0:

    $ zmodload zsh/stat
    $ ls -ld /dev/**/*(-D%be:'zstat -H s $REPLY && (($s[rdev] == 8<<8+0))':)
    lrwxrwxrwx 1 root root    6 Aug 23 05:28 /dev/block/8:0 -> ../sda
    lrwxrwxrwx 1 root root    9 Aug 23 05:28 /dev/disk/by-id/ata-KINGSTON_SNV455S234GB_07MA10014418 -> ../../sda
    brw-rw---- 1 root disk 8, 0 Aug 23 05:28 /dev/sda
    
  • for char device 226:0:

    $ ls -ld /dev/**/*(-D%ce:'zstat -H s $REPLY && (($s[rdev] == 226<<8+0))':)
    lrwxrwxrwx  1 root root      12 Aug 23 05:28 /dev/char/226:0 -> ../dri/card0
    crw-rw----+ 1 root video 226, 0 Aug 23 05:28 /dev/dri/card0
    

Note that anything can create files in /dev. In the very old days, it was a script creating static files in there. At some point, you even had a special file system à la /proc.

On modern versions of Linux, it's usually udev based on input from the kernel.

The name it chooses for the base device file is based on the DEVNAME supplied by the kernel. udev rules may change that but generally don't, and some udev rules will add some more symlinks for convenience (like the /dev/disk/by... ones).

You can go from major:minor to kernel DEVNAME by looking at:

$ sed -n 's/^DEVNAME=//p' /sys/dev/block/8:0/uevent
sda
$ sed -n 's/^DEVNAME=//p' /sys/dev/char/226:0/uevent
dri/card0

You can also get that information from the udev database as mikeserv has shown.

share|improve this answer

Apparently it can be done more simply with udevadm, and I've just found out how.

To get the DEVNAME from udevadm you need only do:

udevadm info -rq name $PATH

For instance, if you wanted to know the /dev name for /sys/dev/char/5:1 you'd do:

udevadm info -rq name /sys/dev/char/5:1

OUTPUT

/dev/console

The -r option is to specify a --rooted path - without it the result above would read only console. The -q option specifies a database --query and it takes the operand name here - because we want the DEVNAME.

A very simple means of finding the path to a char and/or block device given only the major:minor numbers might look like:

mmdev() for d in /sys/dev/[cb]*/$1:$2
        do  [ -e "$d" ] || return
            printf %c:%s: "${d#/*/*/}" "${d##*/}"
            udevadm info -rq name "$d"
        done

So running:

mmdev 8 0

prints...

b:8:0:/dev/sda

Here's the first one I wrote.

majminpath() {
    set -- ${1##*[!0-9]*} ${2##*[!0-9]*}
    udevadm info --export-db |
    sed 's|^[^=]*DEVNAME=||
         \|^[^/]|!h;/MAJOR=/N
         \|='"$1\n.*=${2?}"'$|!d;g'
}

This just scans udevadm --export-db output for the matching numbers. The output looks like:

P: /devices/virtual/vc/vcsa4
N: vcsa4
E: DEVNAME=/dev/vcsa4
E: DEVPATH=/devices/virtual/vc/vcsa4
E: MAJOR=7
E: MINOR=132
E: SUBSYSTEM=vc

P: /devices/virtual/vc/vcsa5
N: vcsa5
E: DEVNAME=/dev/vcsa5
E: DEVPATH=/devices/virtual/vc/vcsa5
E: MAJOR=7
E: MINOR=133
E: SUBSYSTEM=vc

#...and so on

The workflow is like:

  • attempt to strip the [^=]*DEVNAME= string from the head of each line

  • if a line does not have a first character or its first character is / copy that line over hold space

  • if a line matches MAJOR= append Next input line to pattern space

  • if there are 2 lines in pattern space that match =$1\n.*=$2$ then copy hold space over pattern space and auto-print; else delete pattern space

So if I do:

majminpath 7 133 ; majminpath 8 0 ; majminpath 8 1

OUTPUT

/dev/vcsa5
/dev/sda
/dev/sda1

But, as @xae points out, block/char type devices can share maj:min combinations, and so this might possibly print more than one path per call.

share|improve this answer
1  
Unfortunately it's no so easy, a block and a character device can share the same major number. Take a look at the file /proc/devices. –  xae Aug 23 at 19:55
    
I gotta check subsystem - that's right. Thanks, @xae. –  mikeserv Aug 23 at 20:03

Your Answer

 
discard

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.