Отличить обычный файл от символической ссылки

22

Я пишу сценарий bash, который должен отличать обычный файл от символической ссылки. Я думал, что смогу сделать это с выражением if / test, но оно не работает так, как я ожидал:

$ 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

Почему это? И как я могу правильно сделать это?

Нупраптор
источник

Ответы:

20

Похоже, вы просто немного тестируете свои тесты. Вам не нужно запускать оба теста, единственное, что вам нужно для этого случая, это -hтот, который сообщает вам, является ли файл символической ссылкой.

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

-fТест только говорит вам , если объект является файлом. Это вернуло 0бы, если бы это был каталог или узел устройства или символическая ссылка на каталог, но вернется 1на символическую ссылку на файл.

Если вам также необходимо знать, является ли это символической ссылкой на файл, а не на каталог, вам нужно будет объединить результаты обоих тестов с небольшим количеством логики.

Калеб
источник
Как я понял из документов, разница между -eи -fзаключалась в том, что -eона использовалась для того, чтобы узнать, существует ли файл (любого типа), и -fбыла предназначена для проверки того, существует ли файл и является ли он обычным файлом. Кажется, я неправильно понял, что такое «обычный файл» ..
Nupraptor
1
@Nupraptor: Да, вы неправильно поняли документы. Символическая ссылка считается обычным файлом, в отличие от некоторых других типов узлов, которыми может быть файл (узел блочного устройства, узел символьного устройства, каталог и т. Д.). Если вы хотите узнать, какой это тип файла, вам нужно выполнить тест, специфичный -hдля типа файла, например, для символических ссылок, -pдля именованных каналов и т. Д.
Caleb
Затем, как мне проверить, является ли файл обычным файлом в том смысле, что он не является каналом, символической ссылкой и т. Д.? Должен ли я открыть еще один вопрос для этого?
Нупраптор
@Nupraptor: Единственный странный случай - это символическая ссылка, которая ссылается на обычный файл. В противном случае, если он тестируется как обычный файл, это обычный файл.
Дэвид Шварц
3
папка test -f вернет 1, а не 0: test -f. ; echo $? (выходы 1)
полином
7

@Caleb правильно делает сценарий только для проверки символической ссылки. Однако часть о том, почему был исключен, и мне было любопытно. Если вы посмотрите на исходный код coreutils и ограничите вывод теста, вы увидите, что когда вы запускаете тест символьной ссылки, он использует lstat, а если вы используете тест -f, он на самом деле вызывает 'stat', следующий за символической ссылкой:

$ 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

Из справочной страницы 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.

Это означает, что тест -f будет возвращать true, если указанное имя файла является символической ссылкой на обычный файл или сам обычный файл.

многочлен
источник