20

I am looking for a quick way to find the mount point of the file system containing a given FILE. Is there anything simpler or more direct than my solution below?

df -h FILE |tail -1 | awk -F% '{print $NF}' | tr -d ' '

A similar question "Is there a command to see where a disk is mounted?" uses the current disk's device node as input, and not an arbitrary file from the disk ...

Stu
  • 303

5 Answers5

24

On GNU/Linux, if you have GNU stat from coreutils 8.6 or above, you could do:

stat -c %m -- "$file"

Otherwise:

mount_point_of() {
  f=$(readlink -e -- "$1") &&
    until mountpoint -q -- "$f"; do
      f=${f%/*}; f=${f:-/}
    done &&
    printf '%s\n' "$f"
}

Your approach is valid but assumes the mount point doesn't contain space, %, newline or other non-printable characters, you can simplify it slightly with newer versions of GNU df (8.21 or above):

df --output=target FILE | tail -n +2
14

For Linux we have findmnt from util-linux exactly made for this

findmnt -n -o TARGET --target /path/to/FILE

Note that some kind of random mountpoint may be returned in case there are several bind mounts. Using df has the same problem.

rudimeier
  • 10,315
7

You could do something like

df -P FILE | awk 'NR==2{print $NF}'

or even

df -P FILE | awk 'END{print $NF}'

Since awk splits on whitespace(s) by default, you don't need to specify the -F and you also don't need to trim the whitespace with tr. Finally, by specifying the line number of interest (NR==2) you can also do away with tail.

terdon
  • 242,166
  • the 2nd incantation worked out-of-the-box, while I had to change 2 to 3 in the first. neat – Stu Sep 11 '13 at 22:55
  • @Gilles, thanks for the edit. One question, the second should work even without -P right? In all cases, the very last field printed by awk should be the disk. – terdon Sep 11 '13 at 23:34
  • @Stu that's probably because I had not used the -P option that Gilles just added. – terdon Sep 11 '13 at 23:35
  • 1
    @terdon Yes, indeed, the last field of the last line is the same without -P. Nonetheless I recommend always using -P when you're parsing the output of df, it's easier than checking that this particular use is safe. – Gilles 'SO- stop being evil' Sep 11 '13 at 23:37
  • nice. if you need to now the /dev entry for ${FILE} (for some reason) mount | grep " on $(df -P ${FILE} | awk 'END{print $NF}') type" | awk '{print $1}' – unsynchronized Jun 05 '16 at 04:43
  • Not working correctly if FILE is under a subvolume (in BTRFS filesystem). – ceremcem Jun 10 '17 at 20:30
4

Since stat returns a "Device" field, I was curious to see how the underlying stat() library call could be used to get this information programmatically in a POSIX compliant way.

This snippet of C code:

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>

int main (int argc, const char *argv[]) {
    struct stat info;
    stat(argv[1], &info);
    printf("min: %d maj: %d\n",
        minor(info.st_dev),
        major(info.st_dev)
    );

    return 0;
}  

Will give the major and minor devices ID's for the device containing the file listed on the command line (argv[1]). Unfortunately, major() and minor() aren't POSIX, although the man page claims they are "present on many other systems " besides GNU/linux.

You can then get a correspondence between the device major/minor number and the device node from, e.g., /proc/diskstats, and map that to mount points from /proc/mounts, aka. /etc/mtab.

So a command-line utility to do this would be pretty simple.

goldilocks
  • 87,661
  • 30
  • 204
  • 262
  • /proc/diskstats is only for block devices, you'll miss the NFS, proc, fuse... On Linux at least, different mountpoints can have the same maj+min – Stéphane Chazelas Sep 11 '13 at 15:28
  • Did not know that, thx. It also seems that st_dev may not provide a way to distinguish one NFS partition from another. Whoever actually wants to write this will have to take that into account ;) – goldilocks Sep 11 '13 at 15:30
  • +1 for being enough of a geek that you consider writing C code "simpler or more direct" than what the OP was doing :). – terdon Sep 12 '13 at 01:19
3

Here's more C++ code if you want to do this from C++...

  #include <boost/filesystem.hpp>
  #include <sys/stat.h>

  /// returns true if the path is a mount point
  bool Stat::IsMount(const std::string& path)
  {

      if (path == "") return false;
      if (path == "/") return true;

      boost::filesystem::path path2(path);
      auto parent = path2.parent_path();

      struct stat sb_path;
      if (lstat(path.c_str(), &sb_path) == -1) return false; // path does not exist
      if (!S_ISDIR(sb_path.st_mode)) return false; // path is not a directory

      struct stat sb_parent;
      if (lstat(parent.string().c_str(), &sb_parent) == -1 ) return false; // parent does not exist

      if (sb_path.st_dev == sb_parent.st_dev) return false; // parent and child have same device id

      return true;

  }

  /// returns the path to the mount point that contains the path
  std::string Stat::MountPoint(const std::string& path0)
  {
      // first find the first "real" part of the path, because this file may not exist yet
      boost::filesystem::path path(path0);
      while(!boost::filesystem::exists(path) )
      {
          path = path.parent_path();
      }

      // then look for the mount point
      path = boost::filesystem::canonical(path);
      while(! IsMount(path.string()) )
      {
          path = path.parent_path();
      }

      return path.string();
  }

More links for programmatic ways