4

What is the meaning of the nest code

foreach (@items)
{
    if (-l $_)    ## this is what I don't understand: the meaning of -l
    {
        ...
    }
}

Thanks for any help.

Greg Bacon
  • 134,834
  • 32
  • 188
  • 245
adir
  • 1,257
  • 2
  • 16
  • 22

4 Answers4

6

It's an operator that checks if a file is a symbolic link.

ikegami
  • 367,544
  • 15
  • 269
  • 518
  • Is there another way to check? – octopusgrabbus Mar 05 '13 at 13:43
  • Purely for curiosity. I maintain some Perl scripts, and am looking for the most "wordy" way to test for something, including opening files, and so on, despite there being shortcuts. – octopusgrabbus Mar 05 '13 at 13:49
  • Almost all of the -X ops use information from [`stat`](http://perldoc.perl.org/functions/stat.html), and `-l` is no exception. Or if you want silly, you could use Inline::Java to call a Java function to do the same. – ikegami Mar 05 '13 at 13:53
  • @octopusgrabbus Are you trying to obfuscate a script, or is this just an attempt at writing more "readable" code? – TLP Mar 05 '13 at 13:59
  • @TLP curiousity + maintenance = obfuscation and optimization – gaussblurinc Mar 05 '13 at 14:03
  • There are ways to open a file using some of the shortcut methods. I don't use Perl much, and can't remember them right now. I prefer to write it out longhand. I was just wondering if there were another way to write `if (-l $_)`. My question was not a comment on anyone's coding style, answers, or anything else. It was just pure curiosity. – octopusgrabbus Mar 05 '13 at 15:23
6

Let's look at each thing:

foreach (@items) {
    ...
}

This for loop (foreach and for are the same command in Perl) is taking each item from the @items list, and setting it to $_. The $_ is a special variable in Perl that is used as sort of a default variable. The idea is that you could do things like this:

foreach (@items) {
    s/foo/bar/;
    uc;
    print;
}

And each of those command would operate on that $_ variable! If you simply say print with nothing else, it would print whatever is in $_. If you say uc and didn't mention a variable, it would uppercase whatever is in $_.

This is now discouraged for several reasons. First, $_ is global, so there might be side effects that are not intended. For example, imagine you call a subroutine that mucked with the value of $_. You would suddenly be surprised that your program doesn't work.

The other -l is a test operator. This operator checks whether the file given is a symbolic link or not. I've linked to the Perldoc that explains all of the test operators.

If you're not knowledgeable in Unix or BASH/Korn/Bourne shell scripting, having a command that starts with a dash just looks weird. However, much of Perl's syntax was stolen... I mean borrowed from Unix shell and awk commands. In Unix, there's a command called test which you can use like this:

if test -L $FILE
then
    ....
fi

In Unix, that -L is a parameter to the test command, and in Unix, most parameters to commands start with dashes. Perl simply borrowed the same syntax dash and all.

Interestingly, if you read the Perldoc for these test commands, you will notice that like the foreach loop, the various test commands will use the $_ variable if you don't give it a variable or file name. Whoever wrote that script could have written their loop like this:

foreach (@items)
{
    if (-l)   ## Notice no mention of the `$_` variable
    {
        ...
    }
}

Yeah, that's soooo much clear!

Just for your information, The modern way as recommended by many Perl experts (cough Damian Conway cough) is to avoid the $_ variable whenever possible since it doesn't really add clarity and can cause problems. He also recommends just saying for and forgetting foreach, and using curly braces on the same line:

for my $file (@items) {
    if ( -l $file ) {
        ...
    }
}

That might not help with the -l command, but at least you can see you're dealing with files, so you might suspect that -l has something to do with files.

Unfortunately, the Perldoc puts all of these file tests under the -X section and alphabetized under X, so if you're searching the Perldoc for a -l command, or any command that starts with a dash, you won't find it unless you know. However, at least you know now for the future where to look when you see something like this: -s $file.

David W.
  • 105,218
  • 39
  • 216
  • 337
  • 1
    `perldoc -f "-X"` works just fine, though. So does navigating to http://perldoc.perl.org/perlfunc.html and searching for `-l`. When you say `"This is now discouraged for several reasons."`, you actually only mention one reason, and one that is not entirely true. `$_` is aliased to that particular loop. So unless you are using subroutines which (accidentally?) for some reason make use of global variables (bad practice), you're fine to use `$_` as much as you please. The reason I would choose is that it more explicitly states what's going on. – TLP Mar 05 '13 at 15:41
  • You said `$_` is special, but the only thing special about it is that it always refers to `$main::_`, something that's not relevant to the OP. – ikegami Mar 05 '13 at 15:42
  • @ikegami `$_` special because it is the default variable that is used by some commands and that's relevant to the OP. Because it is a default variable, there can be side effects in its use. – David W. Mar 06 '13 at 03:57
  • Like you said, what's special is that some functions read it. The side effects happen no matter what var is used. – ikegami Mar 06 '13 at 04:07
  • @TLP Yes, if you know to look for `-X` in Perldoc, you will find it. And, if you use the web brower's built in find text you will find it. And, you will even find it if you do the command line Perldoc command `perldoc -f -l`. However, you won't find it by looking at the function index, and you won't find it if you try to Google _perl function -1_ which is what many people attempt to do. – David W. Mar 06 '13 at 04:09
  • @ikegami Here's a side effect: I have a loop `for @list {`. In my loop I call a subroutine `foo($value);`. The subroutine uses the `$_` variable as a default for various functions. Later on in my program, I noticed that `@list` has been changed, but I can't figure out where it got changed. – David W. Mar 06 '13 at 04:24
  • Same side effect happens for `for my $x (@list)` – ikegami Mar 06 '13 at 04:26
5

The -l filetest operator checks whether a file is a symbolic link.

The way -l works under the hood resembles the code below.

#! /usr/bin/env perl

use strict;
use warnings;

use Fcntl ':mode';

sub is_symlink {
  my($path) = @_;

  my $mode = (lstat $path)[2];
  die "$0: lstat $path: $!" unless defined $mode;

  return S_ISLNK $mode;
}

my @items = @ARGV;
foreach (@items) {
  if (is_symlink $_) {
    print "$0: link: $_\n";
  }
}

Sample output:

$ ln -s foo/bar/baz quux
$ ./flag-links flag-links quux
./flag-links: link: quux

Note the call to lstat and not stat because the latter would attempt to follow symlinks but never identify them!

To understand how Unix mode bits work, see the accepted answer to “understanding and decoding the file mode value from stat function output.”

Community
  • 1
  • 1
Greg Bacon
  • 134,834
  • 32
  • 188
  • 245
4

From perldoc :

 -l File is a symbolic link.
Vaelor
  • 69
  • 2
  • 13