Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Perl file test operator "-l" not detect symlinks?

Tags:

file

symlink

perl

Why does the Perl file test operator "-l" fail to detect symlinks under the following conditions?

System Info

john@testbed-LT:/temp2/test$ uname -a
Linux Apophis-LT 4.13.0-37-generic #42-Ubuntu SMP Wed Mar 7 14:13:23 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

john@testbed-LT:/temp2/test$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 17.10
Release:        17.10
Codename:       artful

Perl Info

john@testbed-LT:/temp2/test$ perl -v

This is perl 5, version 26, subversion 0 (v5.26.0) built for x86_64-linux-gnu-thread-multi (with 56 registered patches, see perl -V for more detail)

Test Resources

john@testbed-LT:/temp2/test$ touch regular_file
john@testbed-LT:/temp2/test$ mkdir dir
john@testbed-LT:/temp2/test$ ln -s regular_file symlink

john@testbed-LT:/temp2/test$ ls -al
total 12
drwxrwxr-x 3 john john 4096 May  6 02:29 .
drwxrwxrwx 6 john john 4096 May  6 02:29 ..
drwxrwxr-x 2 john john 4096 May  6 02:29 dir
-rw-rw-r-- 1 john john    0 May  6 02:29 regular_file
lrwxrwxrwx 1 john john   12 May  6 02:29 symlink -> regular_file

Script Containing Failing "-l" Operator

john@testbed-LT:/temp2/test$ cat ~/.scripts/test.pl
#!/usr/bin/perl

use strict;
use warnings;
use Cwd 'abs_path';

my $targetDir = "/temp2/test";
opendir(DIR, $targetDir) || die "Can't open $targetDir: $!";

while (readdir DIR) {
    my $file = "$_";

    if($file =~ m/^\.{1,2}/) {
        next;
    }

    $file = abs_path($file);

    if(-l "$file") {
        print "Link: $file\n";
    }
    elsif(-d "$file") {
        print "Dir: $file\n";
    }
    elsif(-f "$file") {
        print "File: $file\n";
    }
    else {
        print "\n\n *** Unhandled file type for file [$file]!\n\n";
        exit 1;
    }
}

closedir(DIR);

Script Output

john@testbed-LT:/temp2/test$ perl ~/.scripts/test.pl
File: /temp2/test/regular_file
Dir: /temp2/test/dir
File: /temp2/test/regular_file

Problem I'm Trying to Solve

Note in the above output that the symlink (named "symlink") is not listed while the file, "regular_file," is listed twice (I want "symlink" listed -- the actual link and not the file it points to).

When I change ... if(-l "$file") ... to ... if(lstat "$file") ... in the script, again "symlink" is not listed while "regular_file" is listed twice, but they are being listed from within the block meant to catch symlinks, i.e.:

john@testbed-LT:/temp2/test$ perl ~/.scripts/test.pl
Link: /temp2/test/regular_file
Link: /temp2/test/dir
Link: /temp2/test/regular_file

Goal

The output I'm trying to achieve (which is faked below -- not actually generated by the script, but by hand) is:

john@testbed-LT:/temp2/test$ perl ~/.scripts/test.pl
File: /temp2/test/regular_file
Dir: /temp2/test/dir
Link: /temp2/test/symlink

...but not necessarily in that order (I don't care about the order of the listing).

Why is the above-shown script not achieving the above-stated goal (why is the "-l" operator not working)?

like image 865
John Doe Avatar asked Dec 18 '22 23:12

John Doe


2 Answers

perldoc Cwd:

abs_path

my $abs_path = abs_path($file);

Uses the same algorithm as getcwd(). Symbolic links and relative-path components ("." and "..") are resolved to return the canonical pathname, just like realpath(3). On error returns undef, with $! set to indicate the error.

(Emphasis mine.)

If you want to see symlinks, don't use abs_path.

What you want to do instead is

$file = "$targetDir/$file";

i.e. prepend the name of the directory you read $file from.


Additional notes:

opendir(DIR, $targetDir) || die "Can't open $targetDir: $!";

while (readdir DIR) {
    my $file = "$_";

should be

opendir(my $dh, $targetDir) || die "Can't open $targetDir: $!";

while (my $file = readdir $dh) {
  • Why use bareword filehandles when you can just use normal variables (that are scoped properly)?
  • There's no reason to quote "$_" here.
  • Why first assign to $_ when you're just going to copy the string to $file in the next step?
like image 91
melpomene Avatar answered Dec 29 '22 11:12

melpomene


Note in the above output that the symlink (named "symlink") is not listed while the file, "regular_file," is listed twice

Yeah, because you used abs_path to turn symlink into /temp2/test/regular_file. Get rid of that line.


By the way, you are missing

$file = "$targetDir/$file";

The only reason your program worked without it is because $targetDir happened to be the current work directory.

like image 24
ikegami Avatar answered Dec 29 '22 11:12

ikegami