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

I have a script which accesses a bunch of other machines using:

@list = `ssh machine "ls path"`;

The problem is that while most of the machines are stable, some might not be running on a particular day. Is there a way to set a timer to send a signal that I can catch if the `ssh` hasn't returned in, say, ten seconds so that the script doesn't wait for the three minutes it'd normally take to time out?

Thanks,

Sean.

Replies are listed 'Best First'.
Re: Timing out ``?
by kennethk (Abbot) on Jun 06, 2012 at 20:49 UTC
    alarm will do what you need -- the link has a good example. Not to be combined with sleep.

    Alternatively, you can put an explicit timeout on the ssh call: @list = `ssh machine -o ConnectTimeout=$timeout "ls path"`; However, in this scenario, you won't know if the connection timed out or not.

    Side note: I don't think @list = `ssh machine "ls path"`; does what you think it does; maybe you mean @list = split /\n/, `ssh machine "ls path"`;?


    #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

      Side note: I don't think @list = `ssh machine "ls path"`; does what you think it does; maybe you mean @list = split /\n/, `ssh machine "ls path"`;?

      Yes it does:

      In scalar context, it comes back as a single (potentially multi-line) string, or undef if the command failed. In list context, returns a list of lines (however you've defined lines with $/ or $INPUT_RECORD_SEPARATOR), or an empty list if the command failed.
      perlop

      So while the OP's way is safe, you'd even have to check for undef before splitting to avoid warnings on failure---which will even be fairly frequent in his use case.

        Except that all of your list entries will have trailing newlines (see perl -e '@list = `ls`; print $list[0] =~ /\n/ ? "yes\n" : "no\n"') which can cause funny errors depending on how you use the list. I would actually consider a warning on failure to be a good thing in this case, although the emitted warning would be unfortunately ambiguous. But this is all fairly subjective, particularly without seeing how the list gets used.

        #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

      To give a concrete example of that:

      use warnings; my @list = (); eval { local $SIG{ALRM} = sub { die "SSH timed out" }; alarm 5; #If after 5 seconds nothing happens, then call SIGALRM @list = `ssh machine "ls path"`; alarm 0; #Reset }; if( $@ ) { warn "I could not do that: $@"; } else { do_something_with( @list ); }

      Update: After being corrected, I have changed the code to make it work

      ~Thomas~
      confess( "I offer no guarantees on my code." );
        No. As the docs say,
        If you want to use alarm to time out a system call you need to use an eval/die pair.
        Your code will emit a warning, but will continue to hang until the backticks return.

        Update: Parent's code has been corrected.

        #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

        This is exactly what I was looking for. I've done similar things in C, just wasn't sure how to handle it in Perl.

        Thanks,

        Sean.