Perl's file test operator -f returns true for symbolic links Perl's file test operator -f returns true for symbolic links unix unix

Perl's file test operator -f returns true for symbolic links


When you test a symlink, the test is carried out on the thing that the symlink points to unless you use the -l symlink test.

This parallels the stat and lstat Linux system-calls which behave similarly. That is, if you stat a symlink, you'll get the result for the target of the symlink, whereas if you lstat the symlink, you'll get the result for the symlink itself. This behaviour is intentional so that naïve programs don't have to care about symlinks, and symlinks will just work as intended.

You should find that if your symlink refers to a directory, the -f test is false and the -d test is true.


Quick solutions

$ ls | perl -lne 'print if stat && -f _'12345file1$ ls | perl -lne 'print if lstat && -f _'file1

Symbolic links and find

By default, GNU find never dereferences or follows symbolic links, but the find documentation describes switches that change this policy.

The options controlling the behaviour of find with respect to links are as follows :-

-P
find does not dereference symbolic links at all. This is the default behaviour. This option must be specified before any of the file names on the command line.

-H
find does not dereference symbolic links (except in the case of file names on the command line, which are dereferenced). If a symbolic link cannot be dereferenced, the information for the symbolic link itself is used. This option must be specified before any of the file names on the command line.

-L
find dereferences symbolic links where possible, and where this is not possible it uses the properties of the symbolic link itself. This option must be specified before any of the file names on the command line. Use of this option also implies the same behaviour as the -noleaf option. If you later use the -H or -P options, this does not turn off -noleaf.

-follow
This option forms part of the “expression” and must be specified after the file names, but it is otherwise equivalent to -L. The -follow option affects only those tests which appear after it on the command line. This option is deprecated. Where possible, you should use -L instead.

Converting find commands to Perl

The standard distribution comes with a find2perl utility that is compatible with find from older Unix systems.

$ find2perl . -type f | perl./file1

We could instead ask for files that are either plain files themselves or links to plain files.

$ find2perl . -follow -type f | perl./1./2./3./4./5./file1

In the code find2perl generates, the default wanted sub passed to find from the File::Find module is

sub wanted {    my ($dev,$ino,$mode,$nlink,$uid,$gid);    (($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_)) &&    -f _    && print("$name\n");}

but with -follow, we get

sub wanted {    my ($dev,$ino,$mode,$nlink,$uid,$gid);    (($dev,$ino,$mode,$nlink,$uid,$gid) = stat($_)) &&    -f _    && print("$name\n");}

Notice that the only difference is whether wanted calls stat or lstat, where the latter is documented as

lstat EXPR
lstat

Does the same thing as the stat function (including setting the special _ filehandle) but stats a symbolic link instead of the file the symbolic link points to. If symbolic links are unimplemented on your system, a normal stat is done. For much more detailed information, please see the documentation for stat.

If EXPR is omitted, stats $_.

As the sample outputs from find2perl shows, you can express your intent with the filetest operator but be precise about the semantics of symlinks with your choice of stat versus lstat.

That funny _ token

The _ at the ends of the quick solutions above is the special filehandle that the lstat documentation mentions. It holds a copy of the most recent result from stat or lstat as a way to avoid having to repeatedly make those expensive system calls. Filetest operators such as -f, -r, -e, and -l also fill this buffer:

If any of the file tests (or either the stat or lstat operator) is given the special filehandle consisting of a solitary underline, then the stat structure of the previous file test (or stat operator) is used, saving a system call. (This doesn't work with -t, and you need to remember that lstat and -l leave values in the stat structure for the symbolic link, not the real file.) (Also, if the stat buffer was filled by an lstat call, -T and -B will reset it with the results of stat _). Example:

print "Can do.\n" if -r $a || -w _ || -x _;stat($filename);print "Readable\n" if -r _;print "Writable\n" if -w _;print "Executable\n" if -x _;


By default all the file test operators (apart from -l) use stat() to test, which means they are transparent to symlinks. -f returns true on a regular file, or a symlink to a regular file.

In order to use lstat() instead, you should first lstat then use the file tests on the special _ filehandle, which stores the results from the most recent stat or lstat operation.

perl -e 'while(<>) { chomp; print "$_\n" if lstat $_ && -f _ }'