unix - simbolicos - Recursión de directorios y enlaces simbólicos
no se permiten enlaces fuertes para directorios (5)
Si recorre recursivamente un árbol de directorios por el método obvio, tendrá problemas con la recursión infinita cuando un enlace simbólico apunta a un directorio principal.
Una solución obvia sería simplemente buscar enlaces simbólicos y no seguirlos en absoluto. Pero eso podría ser una sorpresa desagradable para un usuario que no espera que se ignore silenciosamente lo que se comporta para otros fines, como un directorio perfectamente normal.
Una solución alternativa podría ser mantener una tabla hash de todos los directorios visitados hasta el momento, y usar esto para verificar los bucles. Pero esto requeriría que haya alguna representación canónica, alguna forma de obtener la identidad, del directorio que está mirando actualmente (independientemente de la ruta por la que lo haya alcanzado).
¿Considerarían los usuarios de Unix típicamente que la segunda solución es menos sorprendente?
Si es así, ¿hay alguna forma de obtener una representación canónica / identidad de un directorio, que sea portátil en los sistemas Unix? (Me gustaría que funcione en Linux, BSD, Mac OS, Solaris, etc. Espero tener que escribir un código separado para Windows).
Este problema de archivos idénticos debe ser resuelto por muchas aplicaciones, por ejemplo, un verificador de duplicados de archivos (contenido idéntico, diferentes nombres) y utilidades que actúan en jerarquías de directorios completos, como tar
.
Una buena implementación no querría dar falsos positivos para los archivos vinculados duros y los enlaces simbólicos, ya sea a través de enlaces simbólicos a directorios principales o a archivos.
El enfoque más portátil para resolver esto es identificar los archivos mirando las funciones POSIX stat / fstat y la struct stat
que completan con los miembros st_dev
y st_ino
. Una implementación en el mundo real de un verificador de duplicados de archivos en C que emplea esta estrategia es el mismo archivo (una implementación diferente de la cual fue una entrada ganadora del IOCCC de 1998 :-)
La ruta absoluta del directorio es tal representación. Puede obtenerlo con la función realpath
, que se define en el estándar POSIX, por lo que funcionará en cualquier sistema compatible con POSIX. Ver man 3 realpath
.
No solo enlaces simbólicos, sino enlaces duros también. No es muy común, pero no está prohibido. (Solo el directorio raíz puede enlazar directorios) Lo único que es canónico es {device_number, inode_number}. Pero los sistemas de archivos de red pueden portarse mal.
Como no ha especificado con qué idioma está trabajando (si lo hay), comencemos con solo el shell: si está en un sistema con readlink
GNU, simplemente use readlink -f <path>
para canonicalizarlo.
Si está en una Mac (que tiene un readlink
no GNU que se comporta de manera diferente), consulte ¿Cómo puedo obtener el comportamiento de readlink -f de GNU en una Mac? para la forma de realizar la misma tarea.
La otra opción es usar identificadores de inode para rastrear archivos únicos (a través de stat
o similares), pero eso requerirá primero seguir todos los enlaces simbólicos de todos modos (ya que los enlaces simbólicos sí mismos tienen su propia identificación de inodo), y la forma más sencilla de seguir todos los enlaces simbólicos son, bueno, readlink
.
Alternativamente, muchos lenguajes de programación tienen enlaces a la función POSIX realpath
, que esencialmente realiza la misma función que readlink -f
(pero como una llamada a la biblioteca). Por ejemplo, Python tiene os.path.realpath()
, C lo tiene como una función en stdlib.h
, etcétera.
Si ya está trabajando en un idioma que tiene dicha función, es muy recomendable su uso, ya que a menudo obtendrá compatibilidad multiplataforma de forma gratuita (suponiendo que su idioma sea multiplataforma).
La API ignorada con más frecuencia en este campo sería
nftw
Nftw tiene opciones para evitar que atraviese enlaces simbólicos. Tiene capacidades mucho más avanzadas que eso. Aquí hay una muestra simple de la página man en sí:
#define _XOPEN_SOURCE 500
#include <ftw.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
static int
display_info(const char *fpath, const struct stat *sb,
int tflag, struct FTW *ftwbuf)
{
printf("%-3s %2d %7jd %-40s %d %s/n",
(tflag == FTW_D) ? "d" : (tflag == FTW_DNR) ? "dnr" :
(tflag == FTW_DP) ? "dp" : (tflag == FTW_F) ? "f" :
(tflag == FTW_NS) ? "ns" : (tflag == FTW_SL) ? "sl" :
(tflag == FTW_SLN) ? "sln" : "???",
ftwbuf->level, (intmax_t) sb->st_size,
fpath, ftwbuf->base, fpath + ftwbuf->base);
return 0; /* To tell nftw() to continue */
}
int
main(int argc, char *argv[])
{
int flags = 0;
if (argc > 2 && strchr(argv[2], ''d'') != NULL)
flags |= FTW_DEPTH;
if (argc > 2 && strchr(argv[2], ''p'') != NULL)
flags |= FTW_PHYS;
if (nftw((argc < 2) ? "." : argv[1], display_info, 20, flags)
== -1)
{
perror("nftw");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
Ver también