区分常规文件和符号链接

Nup*_*tor 25 bash

我正在编写一个需要区分常规文件和符号链接的 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
Run Code Online (Sandbox Code Playgroud)

这是为什么?而且,我怎样才能正确地做到这一点?

Cal*_*leb 20

看起来你只是在扰乱你的测试。您不需要同时运行这两个测试,对于这种情况,您唯一需要的是-h告诉您文件是否为符号链接的测试。

test -h file && echo "is symlink" || echo "is regular file"
Run Code Online (Sandbox Code Playgroud)

-f测试仅告诉您对象是否为文件。这将返回0如果它是一个目录或设备节点或一个符号链接到一个目录,但将返回1上一个符号链接到一个文件中。

如果您还需要知道它是否是文件的符号链接而不是目录,则需要将两个测试的结果与一些逻辑结合起来。

  • test -f 目录将返回 1 而不是 0: test -f 。; 回声 $? (输出 1) (3认同)

pol*_*ial 8

@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
Run Code Online (Sandbox Code Playgroud)

从统计手册页:

   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.
Run Code Online (Sandbox Code Playgroud)

这意味着 -f 测试将返回 true,只要指定的文件名是到常规文件或常规文件本身的符号链接。