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

问题描述 投票:0回答:9

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

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

我已经有了完整的路径并且想要得到:

/media/my_mountpoint

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

python linux unix filesystems
9个回答
23
投票

您可以调用

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

7
投票

因为 python 不是必需的:

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

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


6
投票

现在我们无法真正可靠地在通过

mount
UUID
挂载文件系统的系统中解析
LABEL
的内容,因为输出可能包含类似以下内容:

(...)
/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

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

  • %h
    中的硬链接 (
    Talks
    ) 数量为 6
  • 安装点 (
    %m
    ) 是
    /media/lattes
  • 它的索引节点号(
    %i
    )是461246。

仅供记录,这是来自 GNU

coreutils
stat 版本,这意味着某些其他版本(例如 BSD)默认情况下可能没有它(但您始终可以使用您喜欢的软件包安装它)经理)。


2
投票

@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))
}

2
投票

我正在用 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.abspath()
替换
os.path.realpath()
时,我会得到
/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

1
投票

我的 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

0
投票

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

-3
投票
/bin/mountpoint [-q] [-d] /path/to/directory

-4
投票
import os

def find_mount_point(path):
    while not os.path.ismount(path):
        path=os.path.dirname(path)
    return path
© www.soinside.com 2019 - 2024. All rights reserved.