On 11/04/2011 07:48 PM, Ken Peng wrote:
perl -le 'open HD,"/etc/passwd" or die $!; <HD>; print $!'

Here's your one-liner as a script with some "printf" instrumentation:

$ nl HD.pl
     1  #!/usr/bin/perl
     2  use strict;
     3  use warnings;
     4  open HD,"/etc/passwd" or die $!;
     5  print __LINE__, "\n";
     6  <HD>;
     7  print __LINE__, "\n";
     8  print $!;
     9  print __LINE__, "\n";

$ perl HD.pl
5
7
Bad file descriptor9


So, the "Bad file descriptor" message is coming from line 8:

     print $!;


"print" is a function:

    $ perldoc -f print

    print FILEHANDLE LIST
    print LIST
    print   Prints a string or a list of strings. Returns true if
            successful. FILEHANDLE may be a scalar variable name, in
            which case the variable contains the name of or a reference
            to the filehandle, thus introducing one level of
            indirection. (NOTE: If FILEHANDLE is a variable and the next
            token is a term, it may be misinterpreted as an operator
            unless you interpose a "+" or put parentheses around the
            arguments.) If FILEHANDLE is omitted, prints by default to
            standard output (or to the last selected output channel--see
            "select"). If LIST is also omitted, prints $_ to the
            currently selected output channel. To set the default output
            channel to something other than STDOUT use the select
            operation. The current value of $, (if any) is printed
            between each LIST item. The current value of $\ (if any) is
            printed after the entire LIST has been printed. Because
            print takes a LIST, anything in the LIST is evaluated in
            list context, and any subroutine that you call will have one
            or more of its expressions evaluated in list context. Also
            be careful not to follow the print keyword with a left
            parenthesis unless you want the corresponding right
            parenthesis to terminate the arguments to the
            print--interpose a "+" or put parentheses around all the
            arguments.

            Note that if you're storing FILEHANDLEs in an array, or if
            you're using any other expression more complex than a scalar
            variable to retrieve it, you will have to use a block
            returning the filehandle value instead:

                print { $files[$i] } "stuff\n";
                print { $OK ? STDOUT : STDERR } "stuff\n";


"$!" is a predefined special variable:

    $ perldoc perlvar

    $OS_ERROR
    $ERRNO
    $!      If used numerically, yields the current value of the C
            "errno" variable, or in other words, if a system or library
            call fails, it sets this variable. This means that the value
            of $! is meaningful only *immediately* after a failure:

                if (open my $fh, "<", $filename) {
                    # Here $! is meaningless.
                    ...
                } else {
                    # ONLY here is $! meaningful.
                    ...
                    # Already here $! might be meaningless.
                }
                # Since here we might have either success or failure,
                # here $! is meaningless.

            In the above *meaningless* stands for anything: zero,
            non-zero, "undef". A successful system or library call does
            not set the variable to zero.

            If used as a string, yields the corresponding system error
            string. You can assign a number to $! to set *errno* if, for
            instance, you want "$!" to return the string for error *n*,
            or you want to set the exit value for the die() operator.
            (Mnemonic: What just went bang?)

            Also see "Error Indicators".


Checking for "<>" (readline) errors is inobvious:

    $ perldoc -f readline

    readline EXPR
    readline
            Reads from the filehandle whose typeglob is contained in
            EXPR (or from *ARGV if EXPR is not provided). In scalar
            context, each call reads and returns the next line, until
            end-of-file is reached, whereupon the subsequent call
            returns undef. In list context, reads until end-of-file is
            reached and returns a list of lines. Note that the notion of
            "line" used here is however you may have defined it with $/
            or $INPUT_RECORD_SEPARATOR). See "$/" in perlvar.

            When $/ is set to "undef", when readline() is in scalar
            context (i.e. file slurp mode), and when an empty file is
            read, it returns '' the first time, followed by "undef"
            subsequently.

            This is the internal function implementing the "<EXPR>"
            operator, but you can use it directly. The "<EXPR>" operator
            is discussed in more detail in "I/O Operators" in perlop.

                $line = <STDIN>;
                $line = readline(*STDIN);           # same thing

            If readline encounters an operating system error, $! will be
            set with the corresponding error message. It can be helpful
            to check $! when you are reading from filehandles you don't
            trust, such as a tty or a socket. The following example uses
            the operator form of "readline", and takes the necessary
            steps to ensure that "readline" was successful.

                for (;;) {
                    undef $!;
                    unless (defined( $line = <> )) {
                        last if eof;
                        die $! if $!;
                    }
                    # ...
                }


Here's a script we can experiment with:

$ nl HD2.pl
     1  #!/usr/bin/perl
     2  use strict;
     3  use warnings;
     4  for (;;) {
     5      print "\n", __LINE__, " Please enter a file: ";
     6      my $file = <>;
     7      chomp $file;
     8      my $o = open(my $fh, $file);
     9      warn __LINE__, " ignoring: open '$file' failed: $!"
    10          unless $o;
    11      undef $!;
    12      my $line = eval {
    13          local $SIG{__WARN__} = sub {
    14              print STDERR __LINE__, " caught warning: @_";
    15          };
    16          <$fh>;
    17      };
    18      warn __LINE__, "eval failed: $@" if $@;
    19      close $fh or warn __LINE__, " close 'file' failed: $!";
    20      if (defined $line) {
    21          chomp $line;
    22          print __LINE__, " first line: $line\n";
    23      }
    24      else {
    25          if (eof $fh) {
    26              print __LINE__, " end-of-file encountered\n";
    27              next;
    28          }
    29          warn __LINE__, " readline '$fh' failed: $!" if $!;
    30      }
    31  }

$ perl HD2.pl

5 Please enter a file: /etc/passwd
22 first line: root:x:0:0:root:/root:/bin/bash

5 Please enter a file: /dev/null
26 end-of-file encountered

5 Please enter a file: /dev/tty
hello, world!
22 first line: hello, world!

5 Please enter a file: perl -e 'print "blah blah blah"' |
22 first line: blah blah blah

5 Please enter a file: perl -e 'die "bye!"' |
bye! at -e line 1.
19 close 'file' failed:  at HD2.pl line 19.
26 end-of-file encountered

5 Please enter a file: /no/such/file
9 ignoring: open '/no/such/file' failed: No such file or directory at HD2.pl line 9, <> line 6.
14 caught warning: readline() on closed filehandle $fh at HD2.pl line 16.
19 close 'file' failed: Bad file descriptor at HD2.pl line 19.
26 end-of-file encountered

5 Please enter a file: perl -e 'exit 0' |
26 end-of-file encountered

5 Please enter a file: ^C


I was able to force a "readline() on closed filehandle" warning, but the other possibilities would take more research:

    http://www.perlmonks.org/?node=readline+error

    http://www.perlmonks.org/?node_id=781134


So, the bottom line is: I'd use the code from the readline documentation (or something similar) to check for readline errors.


HTH,

David


$ cat /etc/debian_version
6.0.2

$ perl -v

This is perl, v5.10.1 (*) built for i486-linux-gnu-thread-multi
(with 53 registered patches, see perl -V for more detail)

Copyright 1987-2009, Larry Wall

Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5 source kit.

Complete documentation for Perl, including FAQ lists, should be found on
this system using "man perl" or "perldoc perl".  If you have access to the
Internet, point your browser at http://www.perl.org/, the Perl Home Page.


--
To unsubscribe, e-mail: beginners-unsubscr...@perl.org
For additional commands, e-mail: beginners-h...@perl.org
http://learn.perl.org/


Reply via email to