如何找到文件所在的挂载点?

发布于 2024-10-07 10:59:20 字数 207 浏览 4 评论 0原文

例如,我有一个具有以下路径的文件:

/media/my_mountpoint/path/to/file.txt

我有整个路径并想要获取:

/media/my_mountpoint

我该怎么做?最好使用 Python 并且不使用外部库/工具。 (两者都不是必需的。)

For example, I've got a file with the following path:

/media/my_mountpoint/path/to/file.txt

I've got the whole path and want to get:

/media/my_mountpoint

How can I do this? Preferably in Python and without using external libraries / tools. (Both are not a requirement.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

挽清梦 2024-10-14 10:59:20

您可以调用 mount 命令并解析其输出以查找路径中最长的公共前缀,或者使用 stat 系统调用来获取文件所在的设备并沿着树往上走,直到找到不同的设备。

在Python中,stat可以按如下方式使用(未经测试,可能需要扩展以处理符号链接和联合挂载等奇异的东西):

def find_mount_point(path):
    path = os.path.abspath(path)
    orig_dev = os.stat(path).st_dev

    while path != '/':
        dir = os.path.dirname(path)
        if os.stat(dir).st_dev != orig_dev:
            # we crossed the device border
            break
        path = dir
    return path

编辑:我不知道os.path.ismount 直到现在。这大大简化了事情。

def find_mount_point(path):
    path = os.path.abspath(path)
    while not os.path.ismount(path):
        path = os.path.dirname(path)
    return path

You may either call the mount command and parse its output to find the longest common prefix with your path, or use the stat system call to get the device a file resides on and go up the tree until you get to a different device.

In Python, stat may be used as follows (untested and may have to be extended to handle symlinks and exotic stuff like union mounts):

def find_mount_point(path):
    path = os.path.abspath(path)
    orig_dev = os.stat(path).st_dev

    while path != '/':
        dir = os.path.dirname(path)
        if os.stat(dir).st_dev != orig_dev:
            # we crossed the device border
            break
        path = dir
    return path

Edit: I didn't know about os.path.ismount until just now. This simplifies things greatly.

def find_mount_point(path):
    path = os.path.abspath(path)
    while not os.path.ismount(path):
        path = os.path.dirname(path)
    return path
梦年海沫深 2024-10-14 10:59:20

由于 python 不是必需的:

df "$filename" | awk 'NR==1 {next} {print $6; exit}'

NR==1 {next} 是跳过 df 输出的标题行。 $6 是挂载点。 exit 是为了确保我们只输出一行。

Since python is not a requirement:

df "$filename" | awk 'NR==1 {next} {print $6; exit}'

The NR==1 {next} is to skip the header line that df outputs. $6 is the mount point. exit is to make sure we output only one line.

娇纵 2024-10-14 10:59:20

由于现在我们无法真正可靠地解析通过 UUIDLABEL 挂载文件系统的系统中 mount 的内容,因为输出可以包含类似的内容:

(...)
/dev/disk/by-uuid/00000000-0000-0000-0000-000000000000 on / type ext4 (rw,relatime,errors=remount-ro,data=ordered)
(...)

我们需要一个更强大的解决方案(例如,考虑像上面这样的路径的“切割”部分可能会导致什么,以及我们是否想要类似的东西)。

一种这样的解决方案(顺便说一句,尝试不要重新发明轮子)是简单地使用 stat 命令来发现文件所在的挂载点,如下所示:

$ stat --printf "%h:%m:%i\n" Talks
6:/media/lattes:461246

在上面的输出中,我们可以看到:

  • Talks 中的硬链接 (%h) 数量为 6
  • 挂载点 (%m) 为 /media/lattes
  • 它的 inode 号 (%i) 是 461246。

仅供记录,这是来自 GNU coreutils,这意味着某些其他版本(例如 BSD)默认情况下可能没有它(但您始终可以使用您首选的包管理器)。

Since nowadays we can't really reliably parse the contents of mount in systems where a filesystem was mounted by UUID or LABEL, as the output can contain something like:

(...)
/dev/disk/by-uuid/00000000-0000-0000-0000-000000000000 on / type ext4 (rw,relatime,errors=remount-ro,data=ordered)
(...)

we need a more robust solution (e.g., think about what "chopping" parts of a path like the above may lead to, and if we would want something like that).

One such solution (which, by the way, tries not to reinvent the wheel) is to simply use the stat command to discover the mountpoint where a file resides, as in:

$ stat --printf "%h:%m:%i\n" Talks
6:/media/lattes:461246

In the output above, we can see that:

  • the number of hardlinks (%h) in Talks is 6
  • the mountpoint (%m) is /media/lattes
  • its inode number (%i) is 461246.

Just for the record, this is with the version of stat from GNU coreutils, which means that some other versions (e.g., the BSDs) may not have it by default (but you can always install it with your preferred package manager).

你穿错了嫁妆 2024-10-14 10:59:20

@larsmans 非常好的答案,这非常有帮助!我已经在 Golang 中我需要的地方实现了这个。

对于对代码感兴趣的人(已针对 OS X 和 Linux 进行过测试):

package main

import (
    "os"
    "fmt"
    "syscall"
    "path/filepath"
)

func Mountpoint(path string) string {
    pi, err := os.Stat(path)
    if err != nil {
        return ""
    }

    odev := pi.Sys().(*syscall.Stat_t).Dev

    for path != "/" {
        _path := filepath.Dir(path)

        in, err := os.Stat(_path)
        if err != nil {
            return ""
        }

        if odev != in.Sys().(*syscall.Stat_t).Dev {
            break
        }

        path = _path
    }

    return path
}

func main() {
    path, _ := filepath.Abs("./")
    dir := filepath.Dir(path)
    fmt.Println("Path", path)
    fmt.Println("Dir", dir)
    fmt.Println("Mountpoint", Mountpoint(path))
}

@larsmans Very good answer, this was very helpfull! I have implemented this in Golang where I needed it.

For people who are interested in the code (this has been tested for OS X and Linux):

package main

import (
    "os"
    "fmt"
    "syscall"
    "path/filepath"
)

func Mountpoint(path string) string {
    pi, err := os.Stat(path)
    if err != nil {
        return ""
    }

    odev := pi.Sys().(*syscall.Stat_t).Dev

    for path != "/" {
        _path := filepath.Dir(path)

        in, err := os.Stat(_path)
        if err != nil {
            return ""
        }

        if odev != in.Sys().(*syscall.Stat_t).Dev {
            break
        }

        path = _path
    }

    return path
}

func main() {
    path, _ := filepath.Abs("./")
    dir := filepath.Dir(path)
    fmt.Println("Path", path)
    fmt.Println("Dir", dir)
    fmt.Println("Mountpoint", Mountpoint(path))
}
浅语花开 2024-10-14 10:59:20

我当时正在用 Python 开发 GTK+ 3 文件管理器,在循环文件时遇到了同样的需求。

我正在使用的计算机有 Linux 和 OS X 分区。当文件管理器应用程序(在 Linux 根分区上运行)尝试对 OS X 分区上的文件进行索引时,它很快就会遇到从“/media/mac-hd/User Guides And Information”到“/图书馆/文档/用户指南和信息。本地化”并窒息。问题是文件管理器正在其自己的文件系统上寻找该链接的绝对目标,而该链接并不存在,而不是安装在 /media/mac-hd 的 OS X 分区。因此,我需要一种方法来识别文件位于不同的安装点上,并将该安装点添加到链接的绝对目标之前。

我从 Fred Foo 的答案中编辑的解决方案开始。它似乎有助于为我试图解决的特定错误提供解决方案。当我调用 find_mount_point('/media/mac-hd/User Guides And Information') 时,它将返回 /media/mac-hd。太好了,我想。

我注意到 insecure 在关于使其与符号链接一起工作的答案下面的评论,并且还注意到他关于 /var/run 的看法是正确的:

为了使您的代码能够使用符号链接,例如 /var/run -> ../run,将 os.path.abspath() 替换为 os.path.realpath() 或 find_mount_point() 将返回“/ ”。

当我尝试用 os.path.realpath() 替换 os.path.abspath() 时,我会得到 /run 的正确返回值> 对于 /var/run。但是我也注意到,在调用 find_mount_point('/media/mac-hd/User Guides And Information') 时,我将不再获得所需的值,因为它现在返回 /

以下是我最终使用的解决方案。或许可以简化一下:

def find_mount_point(path):
    if not os.path.islink(path):
        path = os.path.abspath(path)
    elif os.path.islink(path) and os.path.lexists(os.readlink(path)):
        path = os.path.realpath(path)
    while not os.path.ismount(path):
        path = os.path.dirname(path)
        if os.path.islink(path) and os.path.lexists(os.readlink(path)):
            path = os.path.realpath(path)
    return path

I was working on a GTK+ 3 file manager in Python and came across the same need when looping through files.

The computer I was working on has Linux and OS X partitions. When the file manager application (running on the root Linux partition) would attempt to index the files on the OS X partition, it would quickly come across an absolute symlink from "/media/mac-hd/User Guides And Information" to "/Library/Documentation/User Guides and Information.localized" and choke. The problem was that the file manager was looking for the absolute target of that link on it's own file system where it does not exist instead of the OS X partition mounted at /media/mac-hd. So, I needed a way to identify that a file was on a different mount point and prepend that mount point to the absolute target of the link.

I began with the edited solution in Fred Foo's answer. It seemed to help provide a solution to the specific error I was trying to work around. When I would call find_mount_point('/media/mac-hd/User Guides And Information'), it would return /media/mac-hd. Great, I thought.

I noticed insecure's comment below the answer about making it work with symlinks and also noticed he was correct about /var/run:

To make your code work with symlinks, e.g. /var/run -> ../run, replace os.path.abspath() with os.path.realpath() or find_mount_point() will return "/".

When I tried replacing os.path.abspath() with os.path.realpath(), I would get the correct return value of /run for /var/run. However I also noticed that I would no longer get the value I wanted when calling find_mount_point('/media/mac-hd/User Guides And Information') because it now returned /.

The following is the solution I ended up using. Perhaps it can be simplified:

def find_mount_point(path):
    if not os.path.islink(path):
        path = os.path.abspath(path)
    elif os.path.islink(path) and os.path.lexists(os.readlink(path)):
        path = os.path.realpath(path)
    while not os.path.ismount(path):
        path = os.path.dirname(path)
        if os.path.islink(path) and os.path.lexists(os.readlink(path)):
            path = os.path.realpath(path)
    return path
不再见 2024-10-14 10:59:20

我的 python 很生锈,但是你可以在 perl 中使用类似的东西:

export PATH_TO_LOOK_FOR="/media/path";
perl -ne '@p = split /\s+/; print "$p[1]\n" if "'$PATH_TO_LOOK_FOR'" =~ m@^$p[1]/@' < /proc/mounts

注意 $PATH_TO_LOOK_FOR 周围的 " ' ' " 否则它将无法工作。

//编辑:python解决方案:

def find_mountpoint(path):
    for l in open("/proc/mounts", "r"):
        mp = l.split(" ")[1]
        if(mp != "/" and path.find(mp)==0): return mp

    return None

My python is rusty, however you can use something like this with perl :

export PATH_TO_LOOK_FOR="/media/path";
perl -ne '@p = split /\s+/; print "$p[1]\n" if "'$PATH_TO_LOOK_FOR'" =~ m@^$p[1]/@' < /proc/mounts

notice the " ' ' " around $PATH_TO_LOOK_FOR otherwise it won't work.

//edit : python solution :

def find_mountpoint(path):
    for l in open("/proc/mounts", "r"):
        mp = l.split(" ")[1]
        if(mp != "/" and path.find(mp)==0): return mp

    return None
守不住的情 2024-10-14 10:59:20

os.path.realpath 删除了符号链接,因此更加简洁:

def find_mountpoint(path):
    """Find the non-symlinked mountpoint of a path."""

    path = os.path.realpath(path)

    while not os.path.ismount(path):
        path = os.path.dirname(path)

    return path

os.path.realpath removes symlinks, so this is more concise:

def find_mountpoint(path):
    """Find the non-symlinked mountpoint of a path."""

    path = os.path.realpath(path)

    while not os.path.ismount(path):
        path = os.path.dirname(path)

    return path
戏舞 2024-10-14 10:59:20
/bin/mountpoint [-q] [-d] /path/to/directory
/bin/mountpoint [-q] [-d] /path/to/directory
江湖正好 2024-10-14 10:59:20
import os

def find_mount_point(path):
    while not os.path.ismount(path):
        path=os.path.dirname(path)
    return path
import os

def find_mount_point(path):
    while not os.path.ismount(path):
        path=os.path.dirname(path)
    return path
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文