2

I'm trying to test if a backtick output string (it is a string, right?) contains a substring.

my $failedCoutner = 0;
my $tarOutput = `tar -tvzf $tgzFile`;
print "$tarOutput\n";

my $subStr = "Cannot open: No such file or directory";
if (index($tarOutput, $subStr) != -1)
{
    push(@failedFiles, $tgzFile);
    $failedCounter++;
    print "Number of Failed Files: $failedCounter\n\n\n";
}
print "Number of Failed Files: $failedCounter\n\n\n";

But, this isn't working. It never enters the if statement.

The backtick output:

tar (child): /backup/Arcsight/EDSSIM004: Cannot open: No such file or directory
tar (child): Error is not recoverable: exiting now
tar: Child returned status 2
tar: Error is not recoverable: exiting now

Number of Failed Files: 0

Clearly the substring is in the first line. Why won't it recognize this?

toolic
  • 57,801
  • 17
  • 75
  • 117
geeoph
  • 349
  • 2
  • 4
  • 10
  • 6
    Are you sure that output is actually in `$tarOutput`? My guess is it's actually going to stderr. Try adding something like `print "tar output is [$tarOutput]\n"` to see what actually is in there. – friedo May 21 '13 at 20:44
  • It's always a good thing to print values like `$tarOutput` to make sure they are what you think they are. – Steve P. May 21 '13 at 20:46
  • And if you need to read from both stdout and stderr, try out [IPC::Open3](https://metacpan.org/module/IPC::Open3). – friedo May 21 '13 at 20:46
  • good call, that came back empty! but then why does it print out in my original print statement? – geeoph May 21 '13 at 20:47
  • using Archive::Tar (https://metacpan.org/module/BINGOS/Archive-Tar-1.90/lib/Archive/Tar.pm) is maybe a better idea as checking stderr from a forked tar. – clt60 May 21 '13 at 20:49
  • 3
    @user215654, your original print statement doesn't print that message. The output tar writes to stderr goes directly to your terminal. The output tar writes to stdout goes into `$tarOutput`, and Perl then prints it. – cjm May 21 '13 at 20:54
  • OHHHHHH. Now that I commented out the print statement, I see that... so somehow I need to access stderr. I'm not really understanding how to go about that with the Open3 Module though... – geeoph May 21 '13 at 20:59
  • You should check the exit code, not stdout/stderr. – jordanm May 21 '13 at 21:01
  • @user215654, if you want to stick with backticks, you could just change it to: `tar -tvzf $tgzFile 2>&1`, assuming you're using bash. – friedo May 21 '13 at 21:04

2 Answers2

1

tar, like most programs, writes error messages to STDERR. That's the purpose of STDERR.

Backticks only capture STDOUT.

You could redirect tar's STDERR to its STDOUT, but why not just check its exit code.

system('tar', '-tvzf', $tgzFile);
die "Can't launch tar: $!\n" if $? == -1;
die "tar killed by signal ".($? & 0x7F) if $? & 0x7F;
die "tar exited with error ".($? >> 8) if $? >> 8;

Advantages:

  • Catches all errors, not just one.
  • The output isn't held up until tar finishes before being sent to the screen.
  • It solves the problem of archives with shell metacharacters (e.g. spaces) in their name without invoking String::ShellQuote's shell_quote.
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • Ok, now I want to keep track of the files that produced errors. How can I save those and view them at the end? – geeoph May 22 '13 at 13:06
  • @user215654 assuming that `tar` returns with an exit code of `0` on success, it would be as easy as `push @failed, $tgzFile if $?;` – Brad Gilbert May 23 '13 at 04:24
0

Check if backticks produced an error with $?:

use warnings;
use strict;

my $tarOutput = `tar -tvzf doesnt_exist.tar.gz`;
if ($?) {
    print "ERROR ... ERROR ... ERROR\n";
}
else {
    # do something else
}

__END__

tar (child): doesnt_exist.tar.gz: Cannot open: No such file or directory
tar (child): Error is not recoverable: exiting now
tar: Child returned status 2
tar: Error is not recoverable: exiting now
ERROR ... ERROR ... ERROR
toolic
  • 57,801
  • 17
  • 75
  • 117