Phân biệt một tệp thông thường từ một liên kết tượng trưng


22

Tôi đang viết một tập lệnh bash cần phân biệt một tệp thông thường với một liên kết tượng trưng. Tôi nghĩ rằng tôi có thể làm điều này với biểu thức if / test, nhưng nó không hoạt động như tôi mong đợi:

$ 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

Tại sao vậy? Và, làm thế nào tôi có thể làm điều này đúng?

Câu trả lời:


20

Có vẻ như bạn chỉ đang tranh giành bài kiểm tra của mình một chút. Bạn không cần chạy cả hai bài kiểm tra, điều duy nhất bạn cần cho trường hợp này là bài kiểm tra -hcho bạn biết nếu tập tin là một liên kết tượng trưng.

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

Các -fthử nghiệm chỉ cho bạn biết nếu đối tượng là một tập tin. Điều này sẽ trở lại 0nếu đó là một thư mục hoặc một nút thiết bị hoặc một liên kết tượng trưng đến một thư mục, nhưng sẽ trở lại 1trên một liên kết tượng trưng đến một tập tin.

Nếu bạn cũng cần biết liệu đó có phải là một liên kết tượng trưng đến một tệp chứ không phải là một thư mục hay không, bạn sẽ cần kết hợp các kết quả của cả hai bài kiểm tra với một chút logic.


Theo tôi hiểu từ các tài liệu, sự khác biệt giữa -e-fđược -esử dụng để biết liệu một tệp (thuộc bất kỳ loại nào) có tồn tại hay không và -fđặc biệt để kiểm tra xem tệp có tồn tại và là một tệp thông thường hay không. Có vẻ như tôi đã hiểu nhầm "tập tin thông thường" là gì ..
Nupraptor

1
@Nupraptor: Vâng, bạn đã hiểu nhầm các tài liệu. Một symlink được coi là một tệp thông thường trái ngược với một số loại nút khác mà một tệp có thể là (nút khối thiết bị, nút thiết bị ký tự, thư mục, v.v.). Nếu bạn muốn biết LOẠI tệp nào, bạn phải chạy thử nghiệm cụ thể loại tệp như -hđối với liên kết tượng trưng, -pđối với các đường dẫn có tên, v.v.
Caleb

Sau đó, làm cách nào để kiểm tra xem một tệp có phải là tệp thông thường theo nghĩa không phải là đường ống cũng không phải là liên kết tượng trưng không, v.v? Tôi có nên mở một câu hỏi khác cho điều này?
Nupraptor

@Nupraptor: Trường hợp kỳ lạ duy nhất là một liên kết tượng trưng liên kết đến một tệp thông thường. Mặt khác, nếu nó kiểm tra như một tệp thông thường, thì đó là một tệp thông thường.
David Schwartz

3
thư mục test -f sẽ trả về 1 không phải là 0: test -f. ; tiếng vang $? (đầu ra 1)
đa thức

7

@Caleb là chính xác về việc làm cho tập lệnh chỉ kiểm tra liên kết tượng trưng. Tuy nhiên phần về lý do bị bỏ rơi và tôi tò mò. Nếu bạn nhìn vào mã nguồn coreutils và tìm ra kết quả kiểm tra, bạn có thể thấy rằng khi bạn chạy kiểm tra liên kết tượng trưng, ​​nó sử dụng lstat và nếu bạn sử dụng kiểm tra -f thì nó thực sự gọi là 'stat' theo liên kết tượng trưng:

$ 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

Từ trang stat man:

   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.

Điều này có nghĩa là kiểm tra -f sẽ trả về đúng miễn là tên tệp được chỉ định là một liên kết tượng trưng đến một tệp thông thường hoặc chính một tệp thông thường.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.