Відмінність звичайного файла від символьної посилання


22

Я пишу сценарій bash, який повинен відрізняти звичайний файл від симпосилання. Я думав, що я можу це зробити, якщо / 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: тест -f. ; ехо $? (виходи 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:

   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 повернеться істинним, доки вказане ім'я файлу є символьним посиланням на звичайний файл або сам звичайний файл.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.