¿Cómo encontrar el punto de assembly donde reside un archivo?

Por ejemplo, tengo un archivo con la siguiente ruta:

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

Tengo todo el camino y quiero conseguir:

 /media/my_mountpoint 

¿Cómo puedo hacer esto? Preferiblemente en Python y sin usar bibliotecas / herramientas externas. (Ambos no son un requisito).

Puede llamar al comando mount y analizar su salida para encontrar el prefijo común más largo con su ruta, o usar la llamada del sistema stat para obtener el dispositivo en el que reside el archivo y subir al árbol hasta que llegue a un dispositivo diferente.

En Python, stat se puede usar de la siguiente manera (sin probar y puede tener que extenderse para manejar enlaces simbólicos y cosas exóticas como assemblys de unión):

 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 : no sabía sobre os.path.ismount hasta ahora. Esto simplifica mucho las cosas.

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

Dado que Python no es un requisito:

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

El NR==1 {next} es omitir la línea del encabezado que df genera. $6 es el punto de assembly. exit es asegurarnos de que salgamos solo una línea.

Dado que en la actualidad no podemos analizar de forma confiable el contenido de mount en sistemas en los que UUID o LABEL montaron un sistema de archivos, ya que la salida puede contener algo como:

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

necesitamos una solución más robusta (p. ej., piense en lo que puede provocar “cortar” partes de un camino como el anterior, y si quisiéramos algo así).

Una de estas soluciones (que, por cierto, intenta no reinventar la rueda) es simplemente usar el comando stat para descubrir el punto de assembly donde reside un archivo, como en:

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

En la salida anterior, podemos ver que:

  • el número de enlaces duros ( %h ) en las Talks es 6
  • el punto de assembly ( %m ) es /media/lattes
  • su número de inodo ( %i ) es 461246.

Solo para el registro, esto es con la versión de stat de GNU coreutils , lo que significa que algunas otras versiones (por ejemplo, los BSD) pueden no tenerlo por defecto (pero siempre puede instalarlo con su administrador de paquetes preferido).

Estaba trabajando en un administrador de archivos GTK + 3 en Python y encontré la misma necesidad al recorrer los archivos.

La computadora en la que estaba trabajando tiene particiones de Linux y OS X. Cuando la aplicación del administrador de archivos (que se ejecuta en la partición raíz de Linux) intentaría indexar los archivos en la partición OS X, rápidamente se encontraría con un enlace simbólico absoluto de “/ media / mac-hd / Guías del usuario e información” a “/ Biblioteca / Documentación / Guías de usuario e información. Localizada “y estrangulador. El problema fue que el administrador de archivos estaba buscando el objective absoluto de ese enlace en su propio sistema de archivos donde no existe en lugar de la partición OS X montada en / media / mac-hd. Por lo tanto, necesitaba una forma de identificar que un archivo estaba en un punto de assembly diferente y anteponer ese punto de assembly al objective absoluto del enlace.

Comencé con la solución editada en la respuesta de Fred Foo . Parecía ayudar a proporcionar una solución al error específico que estaba intentando solucionar. Cuando llamaría a find_mount_point('/media/mac-hd/User Guides And Information') , devolvería /media/mac-hd . Genial, pensé.

Noté el comentario inseguro debajo de la respuesta sobre cómo hacerlo funcionar con enlaces simbólicos y también noté que tenía razón sobre / var / run:

Para hacer que su código funcione con enlaces simbólicos, por ejemplo, / var / run -> ../run, reemplace os.path.abspath() con os.path.realpath() o find_mount_point() devolverá “/”.

Cuando intenté reemplazar os.path.abspath() con os.path.realpath() , obtendría el valor de retorno correcto de /run para /var/run . Sin embargo, también me di cuenta de que ya no obtendría el valor que quería al llamar a find_mount_point('/media/mac-hd/User Guides And Information') porque ahora devolvía / .

La siguiente es la solución que terminé usando. Quizás se pueda simplificar:

 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 

Mi python está oxidado, sin embargo, puedes usar algo como esto con 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 

observe el "''" alrededor de $ PATH_TO_LOOK_FOR de lo contrario no funcionará.

// edit: solución 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 

@larsmans Muy buena respuesta, esto fue muy útil! He implementado esto en Golang donde lo necesitaba.

Para las personas que están interesadas en el código (esto ha sido probado para OS X y 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)) } 
 /bin/mountpoint [-q] [-d] /path/to/directory 
 import os def find_mount_point(path): while not os.path.ismount(path): path=os.path.dirname(path) return path