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

A simple, brute force script for handling various kinds of compressed .tar files as well as .zip files, and passing them on to the proper decompressor.

UPDATE: Added $! to the output from croak, thanks to tilly reminding me it could contain useful information about the system() call, and also fixed a bug where an error would never cause the program to die, due to the value of $? being negative on failure.

#!/usr/bin/perl -w use strict; use Carp; (scalar @ARGV) || die "Usage: $0 file(s)\n"; foreach (@ARGV) { if (/\.tar\.gz$|\.tgz$/) { system "tar -zxvf $_"; error('tar') if ($? < 0); } elsif (/\.bz2$/) { system "tar -Ixvf $_"; error('tar') if ($? < 0); } elsif (/\.z$/i) { system "uncompress -dc | tar -xvf - "; error('uncompress') if +($? < 0); } elsif (/\.zip$/) { system "unzip $_"; error('unzip') if ($? < 0); } else { warn "Don't know what to do with $_, moving on\n"; } } sub error { my $call = shift; croak "Error calling $call - $!\n"; }

Replies are listed 'Best First'.
RE: Expand your world
by fundflow (Chaplain) on Aug 20, 2000 at 18:27 UTC
    It seems like you are using `gtar` which is more flexible than the usual `tar` command. I'd go for the following which is more general and will work on many other systems.
    (gtar pipes bzip/gzip on its own anyway so this doesn't have overhead)

    #!/usr/bin/perl -w $_=shift or die "Need file name\n"; my $command="cat $_ | "; # Deal with common shortcuts: s/\.tgz/.tar.gz/g; s/\.tbz/.tar.bz/g; STRIP: { $command .= "gzip -dc |", redo STRIP if s/\.gz$//; $command .="bzip2 -dc |", redo STRIP if s/\.bz2*$//; $command .="uncompress |", redo STRIP if s/\.Z$//; $command .="unarj |", redo STRIP if s/\.arj$//; $command .= "tar tvf - |" if s/\.tar$//; $command .= "ghostview - |" if s/\.ps//; } chop $command; # remove the last pipe system($command)


    Added: open a postscript file as well :)
RE (tilly) 1: Expand your world
by tilly (Archbishop) on Aug 20, 2000 at 10:34 UTC
    Hashes are more efficient to execute than this kind of explicit logic. Consider this untested code:
    #!/usr/bin/perl -w use strict; use vars qw( $cmd_match %decomp_cmd %decomp_handler ); (scalar @ARGV) || die "Usage: $0 file(s)\n"; %decomp_cmd = ( '.tar.gz' => 'tar -zxvf $_', '.tgz' => 'tar -zxvf $_', '.bz2' => 'tar -Ixvf $_', '.z' => 'uncompress -dc $_| tar -xvf -', '.zip' => 'unzip $_', ); # Set up subs foreach (keys %decomp_cmd) { $decomp_handler{$_} = eval qq( sub { if (system "$decomp_cmd{$_}") { die("Cannot run '$decomp_cmd{$_}': $! (ret $?)\n"); } } ); } # Set up match { my $str = join "|", map {quotemeta($_)} keys %decomp_cmd; $cmd_match = qr/$str/; } foreach (@ARGV) { if (/($cmd_match)$/) { $decomp_handler{$1}->(); } else { warn("Don't know what to do with $_\n"); } }
    Definitely overkill here. But you see the concept. It will perform quite well, is easy to extend, and moves all of the logic you should ever want to change into one place.
      I have trouble with your assertion that this will be more efficient. Any gain in perfomance by using a hash instead of an if-else clause is going to be offset by the multiple eval()s. I also contend that it is just as easy to add another elsif as to add another key-value pair to the hash. Anyway, that's just my opinion.
        If you have a long list of arguments, it will be.

        First of all evals don't cost much more than just having had that much code in the first place. Secondly all of the evals take place up front. So it is just like having a longer program.

        But at runtime I have a hash lookup (constant time no matter how large the hash is) rather than repeated tests, So I have traded compile time away for faster run-time behaviour.

        FWIW I first realized the importance of this win when trying to speed up a program that categorized states into ACLI regions for a huge amount of text information. Just moving the logic out of if/elsif/elsif/else type constructs into hash lookups and/or smart REs was an order of magnitude improvement.

        Cheers, Ben