Distinguir un archivo normal de un enlace simbólico

22

Estoy escribiendo un script bash que necesita distinguir un archivo normal de un enlace simbólico. Pensé que podría hacer esto con la expresión if / test, pero no funciona como esperaba:

$ touch regular_file
$ test -f regular_file; echo $?
0
$ test -h regular_file; echo $?
1
$ ln -s regular_file symlink
$ test -h symlink; echo $?
0
$ test -f symlink; echo $?
0

¿Porqué es eso? Y, ¿cómo puedo hacer esto correctamente?

Nupraptor
fuente

Respuestas:

20

Parece que solo estás revolviendo un poco tus pruebas. No necesita ejecutar ambas pruebas, la única que necesita para este caso es la -hque le dirá si el archivo es un enlace simbólico.

test -h file && echo "is symlink" || echo "is regular file"

La -fprueba solo le dice si el objeto es un archivo. Esto devolvería 0si se tratara de un directorio o un nodo de dispositivo o un enlace simbólico a un directorio, pero volverá 1en un enlace simbólico a un archivo.

Si también necesita saber si se trata de un enlace simbólico a un archivo en lugar de un directorio, necesitaría combinar los resultados de ambas pruebas con un poco de lógica.

Caleb
fuente
Como entendí por los documentos, la diferencia entre -ey -ffue que -ese usó para saber si existía un archivo (de cualquier tipo), y -ffue específicamente para probar si el archivo existía y era un archivo normal. Parece que no he entendido lo que era un "archivo normal" ..
Nupraptor
1
@Nupraptor: Sí, entendiste mal los documentos. Un enlace simbólico se considera un archivo normal en lugar de algunos de los otros tipos de nodos que podría ser un archivo (nodo de dispositivo de bloque, nodo de dispositivo de caracteres, directorio, etc.). Si desea saber qué TIPO de archivo es, debe ejecutar una prueba específica de tipo de archivo, como -henlaces simbólicos, -pcanalizaciones con nombre, etc.
Caleb
Entonces, ¿cómo pruebo si un archivo es un archivo normal en el sentido de que no es una tubería ni un enlace simbólico, etc.? ¿Debo abrir otra pregunta para esto?
Nupraptor
@Nupraptor: el único caso extraño es un enlace simbólico que se vincula a un archivo normal. De lo contrario, si se prueba como un archivo normal, es un archivo normal.
David Schwartz
3
El directorio test -f devolverá un 1, no un 0: test -f. ; echo $? (salidas 1)
polinomio
7

@Caleb está en lo correcto al hacer que el script solo pruebe el enlace simbólico. Sin embargo, la parte sobre por qué se quedó fuera y tenía curiosidad. Si observa el código fuente de coreutils y ordena el resultado de la prueba, puede ver que cuando ejecuta la prueba de enlace simbólico usa lstat y si usa la prueba -f, en realidad llama 'stat' que sigue al enlace simbólico:

$ ln -s varnish_config XXX
$ strace -s 2000 test -L XXX 2>&1 | grep XXX
execve("/usr/bin/test", ["test", "-L", "XXX"], [/* 47 vars */]) = 0
lstat("XXX", {st_mode=S_IFLNK|0777, st_size=14, ...}) = 0

$ strace -s 2000 test -L varnish_config 2>&1 | grep varnish
execve("/usr/bin/test", ["test", "-L", "varnish_config"], [/* 47 vars */]) = 0
lstat("varnish_config", {st_mode=S_IFREG|0664, st_size=1046, ...}) = 0

$ strace -s 2000 test -f XXX 2>&1 | grep XXX
execve("/usr/bin/test", ["test", "-f", "XXX"], [/* 47 vars */]) = 0
stat("XXX", {st_mode=S_IFREG|0664, st_size=1046, ...}) = 0

Desde la página de manual de estadísticas:

   stat() stats the file pointed to by path and fills in buf.

   lstat() is identical to stat(), except that if path is a symbolic link,
   then the link itself is stat-ed, not the file that it refers to.

Esto significa que la prueba -f devolverá verdadero siempre que el nombre de archivo especificado sea un enlace simbólico a un archivo normal o un archivo normal en sí mismo.

polinomio
fuente