Retrieving stdout and stderr while using tkx::open to run external commands

137 Views Asked by At

I have managed to run external commands from Tk GUI in Perl (Tkx module) without blocking the GUI.

However, I have difficulty to retrieve messages from stderr and stdout: for most commands, nothing is stored in variables $stdout and $stderr.

What am I missing in my code?

Thanks

use Tkx;
use strict;
use Data::Dumper;

my ($stdout,$stderr);

my $mw = Tkx::widget->new(".");
my $button=$mw->new_ttk__button(-text => "Run", -command => [\&run_command, "systeminfo"]);
$button->g_grid(-column => 0, -row => 0);
my $text = $mw->new_tk__text(-width => 32, -height => 16);
$text->insert("end", "Test\n");
$text->g_grid(-column => 0, -row => 1);

Tkx::MainLoop();
print "STDOUT: $stdout\n\n","-"x24,"\nSTDERR: $stderr\n";


sub run_command {
    my $cmd = shift;
    my $fh = Tkx::open("| $cmd", 'r') or die "$!";
    Tkx::fconfigure($fh, -blocking => 0);
    $stdout.=Tkx::read($fh);
    eval { Tkx::close($fh); };
    $stderr.=$@ if ($@);

}
1

There are 1 best solutions below

1
On

On Linux, I can use Capture::Tiny to get the output of an external command:

use strict;
use warnings;

use Capture::Tiny ();
use Tkx;

my ($stdout,$stderr);

my $mw = Tkx::widget->new(".");
my $button=$mw->new_ttk__button(-text => "Run", -command => [\&run_command, "echo aaa; eeee"]);
$button->g_grid(-column => 0, -row => 0);
my $text = $mw->new_tk__text(-width => 32, -height => 16);
$text->insert("end", "Test\n");
$text->g_grid(-column => 0, -row => 1);

Tkx::MainLoop();

sub run_command {
    my $cmd = shift;
    my ($stdout, $stderr, $exit) = Capture::Tiny::capture {
        system($cmd);
    };
    print "STDOUT: '$stdout'\n";
    print "STDERR: '$stderr'\n";
    print "Exit code: '$exit'\n";
}

Output:

STDOUT: 'aaa
'
STDERR: 'sh: 1: eeee: not found
'
Exit code: '32512'

Edit

To avoid blocking the GUI, develop a small wrapper script instead, for example:

$ cat wrapperl.pl
use strict;
use warnings;
use Capture::Tiny;

my $cmd = shift;

my ($stdout, $stderr, $exit) = Capture::Tiny::capture {
    system($cmd);
};
print "Child is waiting..\n";
sleep 2;
print "STDOUT: '$stdout'\n";
print "STDERR: '$stderr'\n";
print "Exit code: '$exit'\n";

Then use:

sub run_command {
    my $cmd = shift;
    my $fh;
    print "Master: calling command..\n";
    system ("wrapper.pl \"$cmd\" &");
    print "Master: returning to Tkx::Mainloop..\n";
}

Output:

Master: calling command..
Master: returning to Tkx::Mainloop..
Child is waiting..
STDOUT: 'aaa
'
STDERR: 'sh: 1: eeee: not found
'
Exit code: '32512'