Most references that I found while searching for a simple way to
find the device where a particular file is stored pointed to
the
df
command. However, this does not work if the file is managed
by a 'virtual' file system type such as
ecryptfs that
automatically encrypts its stored contents.
me@pc :~$ df file.txt
Filesystem 1K-blocks Used Available Use% Mounted on
/home/me/.Private 237996696 173473232 52433916 77% /home/me
One solution could be to take the 'virtual' filesystem and
use
df
to find its underlying file system etc. until a
'real' file system representing a storage device is found.
me@pc :~$ df /home/me/.Private
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda1 237996696 173473824 52433324 77% /
Another way is based on the full pathname of the file: simply
find the longest prefix of the file's pathname that corresponds
to a mounted device from
/dev.
The
bash script shown below shows how to do this:
- Use the
readlink command to obtain the full 'canonical' pathname
of the input file name.
- Select all /dev devices that are currently mounted
from /etc/mtab.
The second column of /etc/mtab, referenced as $2 in the
awk
program,
contains the mount point. Since the first character of a string is at
index 1 in awk (instead of 0, as in C), the test
(index(fnm, $2) == 1)
succeeds iff fnm, which was imported into the awk
program using -v fnm=$path, has the mount point
$2 as a prefix. In such a case, the mount point path is printed.
- After
sort,
the mount point that is the longest prefix is the last line
of the output, selected using the
tail
command into the $mpoint variable.
- It only remains to retrieve the device mounted on
$mpoint by selecting appropriately from
/etc/mtab.
#!/bin/bash
readonly USAGE="devof file"
function fatal() {
echo "$1" 2>&1; exit 1
}
[[ $# == 1 ]] || fatal "usage: $USAGE"
[[ -e "$1" ]] || fatal "'$1' not a file";
# readlink -f: follow symbolic links
path=$(readlink -f "$1")
# get paths that are mount points of /dev/* devices
# select those mount points that are a prefix of $path
# grab the longest prefix path $mpoint
mpoint=$(egrep '^/dev' /etc/mtab |
awk -v "fnm=$path" \
'{ if (index(fnm, $2) == 1) print $2 }' |
sort | tail --lines=1
)
# now print the device that is mounted on $mpoint
egrep '^/dev' /etc/mtab |
awk -v "m=$mpoint" '{ if ($2 == m ) print $1 }'
If you are interested in learning more about
bash,
I can highly recommend this
Bash Guide, which assumes that you already know the basics, as explained e.g. in this
tutorial.