Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Confusion on syntax of of dimond operator in perl parsing and barewords

Tags:

syntax

perl

I'm very new to perl, so I'm sure my confusion here is simply due to not understanding perl syntax and how it handles bare words. I'm failing to find good answers to my question online though.

I had code I'm refactoring, it use to look like this

@month_dirs = <$log_directory/*>;

I changed $log_directory to be loaded with a config file (AppConfig to be exact). Now instead of exporting $log_directory we output $conf which is an AppConfig object. To access loaded variables you usually make a method call to the variable name so I tried ...

@month_dirs = <$conf->log_directory()."/*">

This fails, because I can't make a method call $conf->log_directory in a location where a barword is expected. Just playing around I tried this instead

 $month_directory_command = $conf->log_directory()."/*";
 @month_dirs = <$month_directory_command>;

This still fails, silently, without any indicator that this is a problem. I tried using a string directly in the diamond but it fails, apparently only barewords, not strings, are accepted by the diamond I'm surprised by that since I'm not allowed to use a string at all, I thought most places Barewords could be used a string could instead, is this simply because most code implements separate logic to accept barewords vs strings, but not required to be implemented this way?

I can make this work by emulating exactly the original syntax

 $month_directory_command = $conf->log_directory();
 @month_dirs = <$month_directory_command/*>;

However, this feels ugly to me. I'm also confused why I can do that, but I can't create a bare word with:

 $bare_word = $conf->log_directory()/*

or

 $month_directory_command = $conf->log_directory();
 $bare_word = $month_directory_command/*;
 @month_dirs = <$bare_word>;     

Why do some variables work for bare words but not others? why can I use a scaler variable but not if it's returned from a method call?

I tried looking up perl syntax on barewords but didn't have much luck describing situations where they are not written directly, but are composed of variables.

I'm hoping someone can help me better understand the bareword syntax here. What defines when I can use a variable as part of a bare word and if I can save it as a variable?

I'd like to figure out a cleaner syntax for using the barword in my diamond operator if one can be suggested, but more then that I'd like to understand the syntax so I know how to work with barewords in the future. I promise I did try hunting this down ahead of time, but without much luck.

Incidentally, it seems the suggestion is to not use barewords in perl anyways? Is there someway I should be avoid barewords in the diamond operator?

like image 709
dsollen Avatar asked Dec 24 '22 11:12

dsollen


1 Answers

You're mistaken that the diamond operator <> only works with barewords:

$ perl -E'say for <"/*">'
/bin
/boot
/dev
...

(In fact, a bareword is just an identifier that doesn't have a sigil and is prohibited by use strict 'subs';, so none of your examples really qualify.)


This:

@month_dirs = <$log_directory/*>;

works because a level of double-quote interpolation is done inside <>, and scalar variables like $log_directory are interpolated.

It's equivalent to:

@month_dirs = glob("$log_directory/*");

This:

@month_dirs = <$conf->log_directory()."/*">

fails because the > in $conf->log_directory() closes the diamond operator prematurely, confusing the parser.

It's parsed as:

<$conf->

(a call to glob) followed by

log_directory()."/*">

which is a syntax error.


This:

$month_directory_command = $conf->log_directory()."/*";
@month_dirs = <$month_directory_command>;

fails because

<$month_directory_command>

is equivalent to

readline($month_directory_command)

and not to

glob("$month_directory_command")

From perldoc perlop:

If what the angle brackets contain is a simple scalar variable (for example, $foo), then that variable contains the name of the filehandle to input from, or its typeglob, or a reference to the same.

[...]

If what's within the angle brackets is neither a filehandle nor a simple scalar variable containing a filehandle name, typeglob, or typeglob reference, it is interpreted as a filename pattern to be globbed, and either a list of filenames or the next filename in the list is returned, depending on context. This distinction is determined on syntactic grounds alone. That means <$x> is always a readline() from an indirect handle, but <$hash{key}> is always a glob().

So you're trying to read from a filehandle ($month_directory_command) that hasn't been opened yet.

Turning on warnings with use warnings 'all'; would have alerted you to this:

readline() on unopened filehandle at foo line 6.

This:

$bare_word = $conf->log_directory()/*;

fails because you're trying to concatenate the result of a method call with a non-quoted string; to concatenate strings, you have to interpolate them into a double quoted string, or use the concatenation operator.

You could do:

$bare_word = $conf->log_directory() . "/*";
@month_dirs = <"$bare_word">;

(although $bare_word isn't a bareword at all, it's a scalar variable.)

Note that:

@month_dirs = <$bare_word>;

(without quotes) would be interpreted as readline, not glob, as explained in perlop above.

In general, though, it would probably be less confusing to use the glob operator directly:

@month_dirs = glob( $conf->log_directory() . "/*" );
like image 97
ThisSuitIsBlackNot Avatar answered Dec 27 '22 10:12

ThisSuitIsBlackNot