Membedakan file biasa dari symlink


22

Saya sedang menulis skrip bash yang perlu membedakan file biasa dari symlink. Saya pikir saya bisa melakukan ini dengan ekspresi if / test, tetapi tidak berfungsi seperti yang saya harapkan:

$ 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

Mengapa demikian? Dan, bagaimana saya bisa melakukan ini dengan benar?

Jawaban:


20

Sepertinya Anda hanya mencoba sedikit tes Anda. Anda tidak perlu menjalankan kedua pengujian, satu-satunya yang Anda butuhkan untuk kasus ini adalah yang -hmemberi tahu Anda jika file tersebut adalah symlink.

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

The -ftes hanya memberitahu Anda jika objek adalah file. Ini akan kembali 0jika itu adalah direktori atau node perangkat atau symlink ke direktori, tetapi akan kembali 1pada symlink ke file.

Jika Anda juga perlu tahu apakah itu symlink ke file daripada direktori, Anda harus menggabungkan hasil dari kedua tes dengan sedikit logika.


Seperti yang saya mengerti dari dokumen, perbedaan antara -edan -fitu yang -edigunakan untuk mengetahui apakah file (dari jenis apa pun) ada, dan -fsecara khusus untuk menguji apakah file tersebut ada dan merupakan file biasa. Sepertinya saya salah mengerti apa itu "file biasa" itu ..
Nupraptor

1
@Nupraptor: Ya, Anda salah mengerti dokumen. Sebuah symlink dianggap sebagai file biasa yang bertentangan dengan beberapa jenis node lainnya yang mungkin dimiliki oleh file (blok perangkat-perangkat, karakter-perangkat node, direktori, dll). Jika Anda ingin tahu jenis file apa itu, Anda harus menjalankan tes jenis file spesifik seperti -huntuk symlink, -puntuk pipa bernama, dll.
Caleb

Lalu, bagaimana saya menguji apakah suatu file adalah file biasa dalam arti bukan pipa atau symlink, dll? Haruskah saya membuka pertanyaan lain untuk ini?
Nupraptor

@Nupraptor: Satu-satunya kasus aneh adalah symlink yang menautkan ke file biasa. Kalau tidak, jika tes sebagai file biasa, itu file biasa.
David Schwartz

3
direktori test -f akan mengembalikan 1 bukan 0: test -f. ; echo $? (keluaran 1)
polinomial

7

@ Caleb benar tentang membuat skrip hanya menguji symlink. Namun bagian tentang mengapa ditinggalkan dan saya ingin tahu. Jika Anda melihat kode sumber coreutils dan mengukur output tes Anda dapat melihat bahwa ketika Anda menjalankan tes tautan simbolik ia menggunakan lstat dan jika Anda menggunakan uji -f itu benar-benar memanggil 'stat' yang mengikuti 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

Dari halaman manual 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.

Ini berarti tes-f akan mengembalikan true selama nama file yang ditentukan adalah symlink ke file biasa atau file biasa itu sendiri.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.