Distinguere un file normale da un collegamento simbolico


22

Sto scrivendo uno script bash che deve distinguere un file normale da un link simbolico. Pensavo di poterlo fare con l'espressione if / test, ma non funziona come mi aspettavo:

$ 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

Perché? E come posso farlo correttamente?

Risposte:


20

Sembra che tu stia solo mescolando un po 'i tuoi test. Non è necessario eseguire entrambi i test, l'unico che serve in questo caso è -hquello di dirti se il file è un collegamento simbolico.

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

Il -ftest ti dice solo se l'oggetto è un file. Ciò restituirebbe 0se si trattasse di una directory o di un nodo del dispositivo o di un collegamento simbolico a una directory, ma restituirà 1un collegamento simbolico a un file.

Se avessi anche bisogno di sapere se si trattava di un collegamento simbolico a un file anziché a una directory, dovrai combinare i risultati di entrambi i test con un po 'di logica.


Come ho capito dai documenti, la differenza tra -eed -fera che -eera usata per sapere se esisteva un file (di qualsiasi tipo) ed -fera specificamente per verificare se il file esisteva ed era un file normale. Sembra che ho capito male che cosa fosse un "file normale" ...
Nupraptor

1
@Nupraptor: Sì, hai frainteso i documenti. Un collegamento simbolico è considerato un file normale al contrario di altri tipi di nodi che un file potrebbe essere (nodo dispositivo a blocchi, nodo dispositivo a caratteri, directory, ecc.). Se vuoi sapere che tipo di file è, devi eseguire un test specifico del tipo di file come -hper i collegamenti simbolici, -pper le pipe denominate, ecc.
Caleb

Quindi, come posso verificare se un file è un file normale nel senso che non è una pipe né un link simbolico, ecc.? Dovrei aprire un'altra domanda per questo?
Nupraptor,

@Nupraptor: l'unico caso strano è un collegamento simbolico che si collega a un file normale. Altrimenti, se viene testato come file normale, è un file normale.
David Schwartz,

3
La directory test -f restituirà un 1 non uno 0: test -f. ; echo $? (uscite 1)
polinomio

7

@Caleb ha ragione a fare in modo che lo script verifichi solo il collegamento simbolico. Tuttavia, la parte sul perché è stata lasciata fuori ed ero curioso. Se guardi il codice sorgente di coreutils e traccia l'output del test puoi vedere che quando esegui il test del link simbolico usa lstat e se stai usando il test -f in realtà chiama 'stat' che segue il link simbolico:

$ 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

Dalla pagina man di stat:

   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.

Ciò significa che il test -f restituirà true fintanto che il nome file specificato è un collegamento simbolico a un file normale o a un file normale stesso.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.