views:

83

answers:

3

I am new to the File::Slurp module, and on my first test with it, it was not giving the results I was expecting. It took me a while to figure it out, so now I am interested in why I was seeing this certain behavior.

My call to File::Slurp looked like this:

my @array = read_file( $file ) || die "Cannot read $file\n";

I included the "die" part because I am used to doing that when opening files. My @array would always end up with the entire contents of the file in the first element of the array. Finally I took out the "|| die" section, and it started working as I expected.

Here is an example to illustrate:

perl -de0

Loading DB routines from perl5db.pl version 1.22
Editor support available.

Enter h or `h h' for help, or `man perldebug' for more help.

main::(-e:1):   0
DB<1> use File::Slurp

DB<2> $file = '/usr/java6_64/copyright'

DB<3> x @array1 = read_file( $file )
0  'Licensed material - Property of IBM.'
1  'IBM(R) SDK, Java(TM) Technology Edition, Version 6'
2  'IBM(R) Runtime Environment, Java(TM) Technology Edition, Version 6'
3  ''
4  'Copyright Sun Microsystems Inc, 1992, 2008. All rights reserved.'
5  'Copyright IBM Corporation, 1998, 2009. All rights reserved.'
6  ''
7  'The Apache Software License, Version 1.1 and Version 2.0'
8  'Copyright 1999-2007 The Apache Software Foundation. All rights reserved.'
9  ''
10  'Other copyright acknowledgements can be found in the Notices file.'
11  ''
12  'The Java technology is owned and exclusively licensed by Sun Microsystems Inc.'
13  'Java and all Java-based trademarks and logos are trademarks or registered'
14  'trademarks of Sun Microsystems Inc.  in the United States and other countries.'
15  ''
16  'US Govt Users Restricted Rights - Use duplication or disclosure'
17  'restricted by GSA ADP Schedule Contract with IBM Corp.'
DB<4> x @array2 = read_file( $file ) || die "Cannot read $file\n";

0  'Licensed material - Property of IBM.
IBM(R) SDK, Java(TM) Technology Edition, Version 6
IBM(R) Runtime Environment, Java(TM) Technology Edition, Version 6

Copyright Sun Microsystems Inc, 1992, 2008. All rights reserved.
Copyright IBM Corporation, 1998, 2009. All rights reserved.

The Apache Software License, Version 1.1 and Version 2.0
Copyright 1999-2007 The Apache Software Foundation. All rights reserved.

Other copyright acknowledgements can be found in the Notices file.

The Java technology is owned and exclusively licensed by Sun Microsystems Inc.
Java and all Java-based trademarks and logos are trademarks or registered
trademarks of Sun Microsystems Inc.  in the United States and other countries.

US Govt Users Restricted Rights - Use duplication or disclosure
restricted by GSA ADP Schedule Contract with IBM Corp.
'

Why does the || die make a difference? I have a feeling this might be more of a Perl precedence question instead of a File::Slurp question. I looked in the File::Slurp module and it looks like it is set to croak if there is a problem, so I guess the proper way to do it is to allow File::Slurp to croak for you. Now I'm just curious why I was seeing these differences.

+6  A: 

The || operator puts its left-hand operand in boolean (scalar) context:

C-style Logical Or

Binary || performs a short-circuit logical OR operation. That is, if the left operand is true, the right operand is not even evaluated. Scalar or list context propagates down to the right operand if it is evaluated.

You could also discover this empirically:

#! /usr/bin/perl

sub lhs {
  my $ctx = wantarray;
  if (defined $ctx) {
    if ($ctx) { "list" }
    else      { "scalar" }
  }
  else { "void" }
}

print lhs || 0, "\n";

Output:

$ ./or-ctx.pl
scalar

To fix your program, you could modify it slightly:

my @array = eval { read_file("/etc/issue") };
die unless @array;

This is necessary only if you want to add to the error message (stored in the special variable $@ after the eval) because read_file will die if something goes wrong.

Greg Bacon
Thanks so much for the clear explanation and example!
BrianH
@BrianH You're welcome! I'm glad it helps.
Greg Bacon
+9  A: 

The boolean or operator || binds tighter than the assignment operator, and puts the function call into a scalar context.

my @array = read_file( $file ) || die "Cannot read $file\n";

That says: try to read the file, and return either the concatenation of the file or the "return" from die as the first element in @array. You don't die because it read the file and returned a true value, albeit a scalar one.

The standard usage is the statement or operator (or ), like so:

my @array = read_file( $file ) or die "Cannot read $file\n";

This tries to assign @array in a list context, and then evaluates the assigned list in a scalar context, yielding the number of items in array. Thus, you assign the array first, and then you do not die because the array has entries.

The second form does not try to assign the "return" from die to @array because the assignment is done first, so @array either holds the lines of the file or is an empty array.

Notice that the documentation for File::Slurp read_file says:

In list context it will return a list of lines (using the current value of $/ as the separator including support for paragraph mode when it is set to ''). In scalar context it returns the entire file as a single scalar. [italics mine]

And gives the following examples:

    my $text = read_file( 'filename' ) ;
    my @lines = read_file( 'filename' ) ;

But these are the simplest cases, and the most basic expressions of context. Assigning to a variable of a certain type does not ensure that @lines would be assigned to in a list context regardless of surrounding code.

Axeman
This explanation helps me as well - Thanks!
BrianH
@BrianH: Some people *love* context once they get the hang of it and some never warm to it. Some people think it's a needless source of confusion in Perl. I like it because it increases expressiveness, at the cost of causing some learning bumps and occasional surprises.
Axeman
`File::Slurp::read_file` croaks by default on error. So the check is unnecessary.
Sinan Ünür
@Sinan Ünür: So the OP just wants to make sure that the process is good and dead.
Axeman
+4  A: 

Your question has been answered already. As a side note, first, keep in mind that or and and are better suited for flow control statements. However, more importantly, the check below

 my @array = read_file( $file ) || die "Cannot read $file\n";

is completely unnecessary. By default, File::Slurp::read_file croaks on error:

err_mode

You can use this option to control how read_file behaves when an error occurs. This option defaults to 'croak'. You can set it to 'carp' or to 'quiet to have no error handling.

Sinan Ünür
+1 Indeed. That's one reason why `File::Slurp` is so handy.
FM