Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

eval failure does not set $@

by wanderedinn (Sexton)
on Jun 06, 2018 at 14:46 UTC ( [id://1216032]=perlquestion: print w/replies, xml ) Need Help??

wanderedinn has asked for the wisdom of the Perl Monks concerning the following question:

Hello all,

We are currently using Net::SFTP::Foreign to put/get files to various servers. Recently we discovered that there are times when putting files is not successful, yet we do not receive a failure. We are building the put command on the fly and then executing 'eval $PutCMD'. Following the eval we are check $@ to verify the eval was successful. When the put fails, $@ is not being set. An example of the value of $PutCmd is:

$ftp->put("$opt_local_dir/$do_file", "$remote_file",copy_perms => 0, copy_time => 0);

Can anyone see why the failure is not detected?

It appears that the connection has dropped from the time we connected until the time we attempt to put the file. Currently, in order to determine if the put has failed, we are retrieving a listing of the remote directory following each put attempt. When the put does fail, the listing also fails. Problem is, we are not properly detecting the put failure.

Thanks for any insights.

Replies are listed 'Best First'.
Re: eval failure does not set $@
by haukex (Archbishop) on Jun 06, 2018 at 15:04 UTC
    Following the eval we are check $@ to verify the eval was successful.

    There was/is a bug in eval that means the eval { ... }; if ($@) { ... } pattern is not reliable (even though it still appears in examples all over the web). It's much better to do eval { ...; 1 } or do { ... };, or use a module like Try::Tiny.

    We are building the put command on the fly and then executing 'eval $PutCMD'.

    I don't quite understand why you're doing it this way - you might get bitten by quoting/escaping issues. Note that you can always just pass an array of arguments to a method, and build that array dynamically. This example should do the same as the command you showed:

    my @args = ("$opt_local_dir/$do_file"); push @args, $remote_file, copy_perms => 0; push @args, copy_time => 0; eval { $ftp->put(@args); 1 } or warn "put failed: ".($@//"unknown error");

    Note that's eval BLOCK, not eval STRING, which means the code contained in the BLOCK is compiled and checked for syntax errors along with the surrounding code, and at runtime just functions as an error catching mechanism.

Re: eval failure does not set $@
by hippo (Bishop) on Jun 06, 2018 at 15:01 UTC
Re: eval failure does not set $@
by perl-diddler (Chaplain) on Jun 06, 2018 at 20:30 UTC
    eval sets "$@" with the output of what you'd normally see perl output if there were errors compiling your program.

    perl normally wouldn't display runtime errors if a subroutine doesn't do what it is supposed to do.

    eval won't either.

    If the routines you are running return a status, then you want to catch that status coming back from eval:

    my $status = eval "3+4";

    $status should be '7'. Only if the code you are executing "dies" or throws a warning, in the perl sense, would eval return something in $@.

    Does that help?

      Only if the code you are executing "dies" or throws a warning, in the perl sense, would eval return something in $@.

      But as I wrote, there are cases where the code in the eval can die, and $@ doesn't get set (Update: or to be more specific, gets clobbered so that it's no longer available after the eval). A simple demo of this bug:

      use warnings; use strict; sub Foo::DESTROY { eval {} } eval { for (bless {}, "Foo") { print "Hello"; die "KABOOM"; } }; print "<$@>\n"; # Where's the kaboom? There was supposed # to be an earth-shattering kaboom!

      Even on Perl 5.26, this only prints "Hello<>". That's why the eval { ...; 1 } or do { ... }; pattern is better - choroba showed how that pattern still works even in the presence of a similar bug on Perls <5.14 here.

      If the routines you are running return a status, then you want to catch that status coming back from eval

      I would suggest writing it this way, where of course 3+4 is a stand-in for something more complex (and it may also be a false value):

      my $status; eval '$status = 3+4; 1' or warn $@||"unknown error";

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1216032]
Approved by toolic
Front-paged by kcott
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others surveying the Monastery: (5)
As of 2024-04-23 20:58 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found