http://qs1969.pair.com?node_id=1171405

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

I need to capture the output of a bash command in perl which I've done a million times but for some reason this specific issue is hanging me up.

What I need to do it capture the output from a git fetch --tags command and parse the results. The issue is if I run the following code

#!/usr/bin/perl use warnings; use strict; my $git = system('git fetch --tags'); print "What's here? --- $git\n";
The output of the command is displayed in terminal (which is to be expected since I haven't redirected it). However, if I run the command as followed:
my $git = system('git fetch --tags &> /dev/null');
The variable is empty (which is also expected because I am redirecting it to /dev/null). The only way I can get it to work is if I redirect to a file first as such:
git fetch --tags &> /tmp/git.txt
and then parsing the output file. I am trying to avoid having to do that if at all possible meaning I do not want an extra step of checking a tmp file, would much rather do it in one step. Help would be greatly appreciated. Thanks

********** UPDATE **********

I managed to solve this issue based on some of your suggestions using Capture::Tiny. The following code is doing exactly what I wanted it to do.

#!/usr/bin/perl use warnings; use strict; use Capture::Tiny ':all'; use Version::Compare; use Sort::Key::Natural qw( natsort ); my $GIT_HOME = '/usr/local/src/Arelle_Web/DQC_Test/'; my (@git) = capture{system("git --git-dir=${GIT_HOME}.git fetch --tags +");}; @git = grep { $_ ne '' } @git; my @tags; if(scalar(@git) > 1){ print "Tags have been updated\n"; my $string = join("\n", @git); @tags = ($string =~ m/\[new tag\]\s+(.+?)\s/g); } else { @tags = split("\n",capture{system("git --git-dir=${GIT_HOME}.git t +ag")}); print "No New Tags\n"; } @tags = natsort @tags; chomp(my $running_version = capture{system("git --git-dir=${GIT_HOME}. +git describe --tag");}); $running_version =~ s/^v//; my ($ver_major) = $running_version =~ /^(\d)/; my @version = grep(/^v$ver_major.+/,@tags); if(@version){ @version = natsort @version; my $newest = pop(@version); $newest =~ s/^v//; if(&Version::Compare::version_compare($newest,$running_version) == + 1){ print "v$newest is greater than the running version v$running_ +version\n"; } }

Thanks again for all your suggestions.

Replies are listed 'Best First'.
Re: Capturing bash output without displaying STOUT in terminal
by stevieb (Canon) on Sep 08, 2016 at 16:08 UTC

    You can use the qx operator or backticks to do pretty much the same thing as system, but it'll capture the STDOUT (it doesn't capture STDERR though).

    use warnings; use strict; my $stdout = qx{ git status }; # or `git status` ...

    See Quote and Quote-Like Operators in perlop.

      The issue is using the backticks or qx, the stdout is still diplayed in the console and nothing is captured in the variable. Maybe it's the specific command I am running.

        What is the output on screen you get?

        Try running something you know won't error out and write to STDERR instead of STDOUT. For instance:

        my $out = `ls`;

        ...should NOT write to STDERR. If that works, but your original command doesn't, I'm pretty certain that something is happening that is writing to STDERR.

        Try doing a git status with backticks and see what happens. Now move the test to a non-repo directory and compare the results (the latter will print to console, the former won't).

Re: Capturing bash output without displaying STOUT in terminal
by dasgar (Priest) on Sep 08, 2016 at 16:10 UTC

    Maybe Capture::Tiny might be able to help you to capture the output of a system command without displaying the output to the terminal.

      Yea, I am looking into Capture::Tiny right now, the output of this command still doesn't seem to want to be captured, I will play around with it some

        Based on your response to my post as well as stevieb's initial post, I'd agree with stevieb that it looks like the command that you're running is sending the response to STDERR. As stevieb has pointed out, his suggestion of using backticks will only let you capture STDOUT. Capture::Tiny can capture both STDOUT and STDERR (and exit code) for system commands.

        Some programs (e.g ssh when asking for a password) write directly to the terminal, not STDOUT.

        $ ssh ikegami@209.197.123.153 2>&1 >/dev/null ikegami@209.197.123.153's password:

        You have to create pseudo-tty's (as Expect does) in that case.


        IPC::Run supports capturing STDOUT and STDERR separately, and easily. It even supports pseudo-tty creation if you need them.

Re: Capturing bash output without displaying STOUT in terminal
by karlgoethebier (Abbot) on Sep 09, 2016 at 08:35 UTC
Re: Capturing bash output without displaying STOUT in terminal
by ikegami (Patriarch) on Sep 08, 2016 at 21:40 UTC

    Note that

    system(EXPR)
    is equivalent
    system('/bin/sh', '-c', EXPR)
    so <c>&> is unlikely to be supported.