Is it possible to convert 7z to tar directly?

I have a .7z file containing ~360,000 images in multiple directories. I'd like to convert it to a .tar so that I can open it in another computer. Is there a better way than extracting it to files and compressing them again? Is it possible to do the conversion directly?


Solution 1:

.7z archives are archives often compressed with some kind of algorithm, while .tar archives are just archives.

They differ in their scope, and in most cases a conversion would require an optional decompression always followed by an extraction of the source archive. Even if a .7z archive wouldn't use any compression it would still require an extraction.

That being said, If you meant to [decompress] / extract / rearchive the source archive at once, the answer is you can't, at least not using Ubuntu's default tools because tar can't read from stdin, so you can't pipe 7z and tar. Anyway it's very easy to automate everything in a single command:

mkdir tmp && 7z x <path_to_source_archive> -otmp && tar cf archive.tar tmp && rm -rf tmp

* <path_to_archive> = path to the source .7z archive

Also the time required for the source archive's files to be written to the disk and for the extracted files to be read in order to [decompress] / extract / rearchive the source archive in two steps is a bottleneck for the whole task mostly (altough not only) because of a potential disk's low I/O speed, so a partial solution would be to store the temporary files to a ramdisk in order to almost void the overall bottleneck:

  1. Create the mount point for the ramdisk's filesystem: sudo mkdir /mnt/tmpfs
  2. Mount the ramdisk's filesystem: sudo mount -t tmpfs -o size=<tmpfs_size> tmpfs /mnt/ramdisk* <tmpfs_size> = size of the filesystem in bytes * 103 (1, 1K, 1M, 1G, ...)
  3. Run mkdir /mnt/tmpfs/tmp && 7z x <path_to_source_archive> -o/mnt/tmpfs/tmp && tar cf archive.tar /mnt/tmpfs/tmp && rm -rf /mnt/tmpfs/tmp* <path_to_archive> = path to the source .7z archive
  4. Unmount the ramdisk's filesystem: sudo umount
  5. Remove the mount point for the ramdisk's filesystem: sudo rmdir /mnt/tmpfs

Solution 2:

It's not too hard to write something to do the job. Here's an example Perl script (requires the module Archive::Libarchive::XS).

#!/usr/bin/perl
use strict;
use warnings;
use Archive::Libarchive::XS qw(:all);

die "Usage: $0 in.7z out.tar" unless @ARGV == 2;
my ($infile, $outfile) = @ARGV;

my $in = archive_read_new();
archive_read_support_filter_none($in);
archive_read_support_format_7zip($in);

archive_read_open_filename($in, $infile, 10240) == ARCHIVE_OK
  or die "Error opening $infile: ", archive_error_string($in);

my $out = archive_write_new();
archive_write_set_format_ustar($out);
archive_write_open_filename($out, $outfile) == ARCHIVE_OK
  or die "Error opening $outfile: ", archive_error_string($out);

while (archive_read_next_header($in, my $entry) == ARCHIVE_OK) {
  archive_write_header($out, $entry) == ARCHIVE_OK
    or die archive_error_string($out);
  while (1) {
    my $size = archive_read_data($in, my $buff, 65536);
    die archive_error_string($in) if $size < 0; 
    last if $size == 0;
    archive_write_data($out, $buff) >= 0
      or die archive_error_string($out);
  }
} 

archive_read_free($in);
archive_write_close($out);
archive_write_free($out);

If you wanted a tar.gz / tar.bz2 / tar.xz archive, add the line archive_write_add_filter_gzip($out); or archive_write_add_filter_bzip2($out); or archive_tar_add_filter_xz($out); after the archive_write_set_format line.

This uses no temporary disk space (just the space for the output tar file) and very little RAM (just a few MB for perl, but it works with the files a block at a time, so it's not a problem if your files are bigger than your RAM).