Как найти точку монтирования файла?
Например, у меня есть файл со следующим путем:
/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