Rename directory inside of a tar archive

tar

Is it possible to rename a directory inside of a tar archive? My use case is that I have an externally provided RPM spec file that assumes a tarball with a certain directory structure, and I have an externally provided tarball whose top-level directory name does not match what the spec file expects. I don't control either the script that generates the tarballs or the RPM spec file, so I can't make a change to either of those to match the other.

What I have been doing is untarring, changing the directory name, and then creating a new tarball, but I was wondering if there was an alternative to doing that.

Best Answer

It shouldn't be very difficult, at least for archives that are compatible with the old-style format where file names are stored in a fixed-size (100 bytes) field, but I don't know of any tool that can rename a file in place in a tar archive. Besides, with a compressed archive, you'd need to create a new file anyway.

It should be even easier, but I don't know of any existing tool that can filter an archive, renaming files as it goes. You can build one on top of tar libraries in scripting languages; for example, here's a proof-of-concept script to rename a directory in a tar archive using Perl with Archive::Tar. The archive is loaded entirely into memory; this is an intrinsic limitation of Archive::Tar.

#!/usr/bin/env perl
## Usage: tar-rename OLDPREFIX NEWPREFIX
use strict;
use warnings;
use Archive::Tar;
my ($from, $to) = @ARGV;
my $tar = Archive::Tar->new(\*STDIN);
foreach my $file ($tar->get_files()) {
    my $name = $file->name;
    $name =~ s~\A\Q$from\E($|/)~$to$1~;
    $file->rename($name) unless $name eq $file->name;
}
$tar->write(\*STDOUT);

GNU tar doesn't have the ability to rename members on the fly, but pax (POSIX's replacement for cpio and tar) does. However, you can't make pax both read and write from an archive. What you can do is expose the archive as a regular tree through AVFS, and create a new archive with pax. This retains file names (except as transformed), contents, times and modes but resets file ownership to you (unless executed as root).

mountavfs
cd "~/.avfs$PWD/old.tgz#"
pax -w -s '!bar!baz!' -s '!bar/!baz/' . | gzip >new.tgz
Related Question