1

I have code that executes system commands:

use strict;
use warnings FATAL => 'all';
use autodie ':default'; # i.e. no "system"
use Devel::Confess 'color';
use Capture::Tiny 'capture';

sub execute {
    my $cmd = shift;
    my ($stdout, $stderr, $exit) = capture {
        system( $cmd )
    };
    
    if ($exit != 0) {
        say "exit = $exit";
        say "STDOUT = $stdout";
        say "STDERR = $stderr";
        die "$cmd failed";
    }
    return 0
}

Based on previous advice that I've received from

How to get Capture::Tiny to print stderr and stdout upon failure?

However, this subroutine doesn't work very well when the output of a command gives some sort of prompt or warning, and just hangs forever instead of printing the error message/prompt.

The hanging normally happens with various LaTeX commands, but because LaTeX doesn't provide for very good minimal working examples, similar hanging with no printed/displayed error is also produced if a file without writing permission is attempted to be deleted,

e.g. chmod a-w tmp

and then calling the script execute('rm tmp') which would ordinarily give this question at the CLI: rm: remove write-protected regular file 'tmp'?

but just hangs with nothing printed at output.

I've tried changing system( $cmd ) to eval { system( $cmd ) } but I still can't see the error messages.

How can I alter the subroutine above so that I get the prompts and errors like when trying to delete a file without write permission?

con
  • 5,767
  • 8
  • 33
  • 62
  • 1
    You print the output after it exits. But it doesn't exit cause it's waiting for input. So it's doing exactly what you asked for. If you want to print the output before the program exits, `system`'s not gonna do the trick. I'd use IPC::Run – ikegami Mar 22 '21 at 15:40
  • @ikegami how can I print the output before `system` exits? I don't see the prompt, given the current code – con Mar 22 '21 at 15:49
  • If you want to print the output before the program exits, `system`'s not gonna do the trick. I'd use IPC::Run. – ikegami Mar 22 '21 at 15:56
  • https://metacpan.org/pod/IPC::System::Simple there's nothing on there that can capture an exit code, stdout, & stderr all at the same time, how can I do this with IPC::System::Simple? – con Mar 22 '21 at 21:21
  • No, I::S:S only provides functions that block waiting for the child – ikegami Mar 22 '21 at 21:35

1 Answers1

2

There are various ways to handle these things, but each technique depends on what the prompt wants.

  • Some commands have options so you don't have to answer questions. For example, rm -f may do what you want.
  • The yes command is an endless list of "yes": yes | rm *.txt
  • likewise, if you know what the answers to the questions are, you can have its input ready, some_command < input.txt.
  • Closing STDIN before you start the command may handle some cases.
  • Some things use environment variables to answer questions, such as PERL_MM_USE_DEFAULT in ExtUtils::Makemaker.
  • Perl has some modules that can handle some of these cases. IPC::Run, for instance, allows you to mix reading and writing to a process. Other options such as IPC::Open3 don't work so well in those cases because the buffer either fills up or the exchange blocks.

There are probably lots of other techniques too.

brian d foy
  • 129,424
  • 31
  • 207
  • 592