Как найти точку монтирования файла?

Например, у меня есть файл со следующим путем:

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

У меня есть весь путь и хочу получить:

/media/my_mountpoint

Как я могу это сделать? Предпочтительно в Python и без использования внешних библиотек/инструментов. (Оба они не являются обязательными.)

Ответы

Ответ 1

Вы можете вызвать команду 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

Ответ 2

Поскольку python не является обязательным требованием:

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

NR==1 {next} - пропустить строку заголовка, выводящую df. $6 - точка монтирования. exit заключается в том, чтобы выводить только одну строку.

Ответ 3

Поскольку в настоящее время мы не можем достоверно анализировать содержимое 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
  • его номер inode (%i) равен 461246.

Только для записи это версия stat из GNU coreutils, что означает, что некоторые другие версии (например, BSD) может не иметь его по умолчанию (но вы всегда можете установить его с вашим предпочтительным менеджером пакетов).

Ответ 4

Я работал над файловым менеджером GTK + 3 в Python и сталкивался с такой же потребностью, когда делал цикл через файлы.

На компьютере, на котором я работал, есть разделы Linux и OS X. Когда приложение файлового менеджера (работающее на корневом разделе Linux) попытается проиндексировать файлы в разделе OS X, оно быстро встретит абсолютную символическую ссылку из раздела "/media/mac-hd/User Guides And Information" на "/Library/Документация/Руководства пользователя и информация .localized" и дроссель. Проблема заключалась в том, что файловый менеджер искал абсолютную цель этой ссылки в своей собственной файловой системе, где она не существует вместо раздела OS X, установленного на /media/mac -hd. Итак, мне нужен способ определить, что файл находится в другой точке монтирования, и добавьте эту точку монтирования в абсолютную цель ссылки.

Я начал с отредактированного решения в ответ Fred Foo. Казалось, это помогло найти решение конкретной ошибки, с которой я пытался работать. Когда я назову find_mount_point('/media/mac-hd/User Guides And Information'), он вернет /media/mac-hd. Отлично, подумал я.

Я заметил небезопасный комментарий ниже ответа о том, как заставить его работать с символическими ссылками, а также заметил, что он прав в /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

Ответ 5

Мой python ржавый, однако вы можете использовать что-то вроде этого с perl:

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

обратите внимание на "" вокруг $PATH_TO_LOOK_FOR, иначе это не сработает.

//edit: решение 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

Ответ 6

@larsmans Очень хороший ответ, это было очень полезно! Я реализовал это в Голанге, где мне это нужно.

Для людей, которые интересуются кодом (это было протестировано для 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))
}

Ответ 7

/bin/mountpoint [-q] [-d] /path/to/directory

Ответ 8

import os

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