แยกแยะไฟล์ปกติจาก symlink


22

ฉันกำลังเขียนสคริปต์ทุบตีที่ต้องการแยกไฟล์ปกติจาก symlink ฉันคิดว่าฉันสามารถทำได้ด้วย if / test expression แต่มันไม่ทำงานอย่างที่ฉันคาดไว้:

$ 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จะบอกคุณว่าไฟล์นั้นเป็น symlink หรือไม่

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

การ-fทดสอบจะบอกคุณว่าวัตถุนั้นเป็นไฟล์หรือไม่ สิ่งนี้จะส่งคืน0หากเป็นไดเร็กทอรีหรือโหนดอุปกรณ์หรือ symlink ไปยังไดเร็กทอรี แต่จะส่งคืน1บน symlink ไปยังไฟล์

ถ้าคุณต้องการทราบว่ามันเป็น symlink ไปยังแฟ้มมากกว่าไดเรกทอรีคุณจะต้องรวมผลลัพธ์ของการทดสอบทั้งสองเข้ากับตรรกะเล็กน้อย


ดังที่ฉันเข้าใจจากเอกสารความแตกต่างระหว่าง-eและ-fที่-eใช้เพื่อทราบว่ามีไฟล์ (ประเภทใด ๆ ) อยู่หรือไม่และ-fเพื่อทดสอบว่าไฟล์นั้นมีอยู่และเป็นไฟล์ปกติหรือไม่ ดูเหมือนว่าฉันเข้าใจผิดว่า "ไฟล์ปกติ" คืออะไร ..
Nupraptor

1
@Nupraptor: ใช่คุณเข้าใจผิดกับเอกสาร symlink ถือเป็นไฟล์ปกติเมื่อเทียบกับโหนดประเภทอื่น ๆ บางไฟล์ที่อาจเป็น (โหนดอุปกรณ์บล็อกโหนดอุปกรณ์ตัวอักษรไดเรกทอรี ฯลฯ ) หากคุณต้องการที่จะรู้ว่าสิ่งที่ประเภทของไฟล์ที่คุณต้องเรียกใช้การทดสอบประเภทของไฟล์ที่เฉพาะเจาะจงเช่น-hสำหรับ symlinks, -pท่อชื่อ ฯลฯ
คาเลบ

จากนั้นฉันจะทดสอบได้อย่างไรว่าไฟล์เป็นไฟล์ปกติในแง่ที่ไม่ใช่ไพพ์หรือ symlink เป็นต้น ฉันควรเปิดคำถามอื่นสำหรับเรื่องนี้หรือไม่?
Nupraptor

@Nupraptor: กรณีที่แปลกเพียงอย่างเดียวคือ symlink ที่เชื่อมโยงไปยังไฟล์ปกติ มิฉะนั้นถ้ามันทดสอบไฟล์ปกติมันเป็นไฟล์ปกติ
David Schwartz

3
ไดเร็กทอรี test -f จะส่งคืน 1 ไม่ใช่ 0: test -f ; echo $ (ผลลัพธ์ 1)
พหุนาม

7

@Caleb ถูกต้องเกี่ยวกับการสร้างสคริปต์เพียงทดสอบ symlink อย่างไรก็ตามส่วนที่เกี่ยวกับสาเหตุที่ถูกทิ้งและฉันก็อยากรู้ หากคุณดูซอร์สโค้ด coreutils และหยุดเอาต์พุตของการทดสอบคุณจะเห็นว่าเมื่อคุณรันการทดสอบลิงก์สัญลักษณ์มันใช้ lstat และหากคุณใช้การทดสอบ -f จะเรียกว่า 'stat' ซึ่งตามด้วย 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

จากหน้าสถิติคน:

   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 จะกลับเป็นจริงตราบใดที่ชื่อไฟล์ที่ระบุเป็น symlink ไปยังไฟล์ปกติหรือไฟล์ปกติเอง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.