Distinguindo um arquivo regular de um symlink

15

Estou escrevendo um script bash que precisa distinguir um arquivo regular de um symlink. Eu pensei que poderia fazer isso com a expressão if / test, mas não funciona como eu esperava:

$ 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

Por que isso? E como posso fazer isso corretamente?

    
por Nupraptor 04.10.2011 / 14:05

2 respostas

14

Parece que você está apenas embaralhando seus testes um pouco. Você não precisa rodar os dois testes, o único que você precisa para este caso é o -h para dizer se o arquivo é um link simbólico.

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

O teste -f informa se o objeto é um arquivo. Isso retornaria 0 se fosse um diretório ou um nó de dispositivo ou um link simbólico para um diretório, mas retornará 1 em um symlink para um arquivo.

Se você também precisava saber se era um link simbólico para um arquivo em vez de um diretório, seria necessário combinar os resultados de ambos os testes com um pouco de lógica.

    
por 04.10.2011 / 14:14
7

@Caleb está correto em fazer o script apenas testar o symlink. No entanto, a parte sobre por que ficou de fora e eu estava curioso. Se você olhar o código fonte do coreutils e strace a saída do teste, você pode ver que quando você executa o teste do link simbólico ele usa lstat e se você estiver usando o teste -f ele realmente chama 'stat' que segue o symlink:

$ 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

Na página do manual de estatí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.

Isso significa que o teste -f retornará verdadeiro, desde que o nome do arquivo especificado seja um link simbólico para um arquivo regular ou um arquivo regular em si.

    
por 06.10.2011 / 05:46

Tags