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.
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.
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
.
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.”