Convert tar.gz to zip

2019-04-28 15:22发布

问题:

I've got a large collection of gzipped archives on my Ubuntu webserver, and I need them converted to zips. I figure this would be done with a script, but what language should I use, and how would I go about unzipping and rezipping files?

回答1:

I'd do it with a bash(1) one-liner:

for f in *.tar.gz;\
do rm -rf ${f%.tar.gz} ;\
mkdir ${f%.tar.gz} ;\
tar -C ${f%.tar.gz} zxvf $f ;\
zip -r ${f%.tar.gz} $f.zip ;\
rm -rf ${f%.tar.gz} ;\
done

It isn't very pretty because I'm not great at bash(1). Note that this destroys a lot of directories so be sure you know what this does before doing it.

See the bash(1) reference card for more details on the ${foo%bar} syntax.



回答2:

A simple bash script would be easiest, surely? That way you can just invoke the tar and zip commands.



回答3:

the easiest solution on unix platforms may well be to use fuse and something like archivemount (libarchive), http://en.wikipedia.org/wiki/Archivemount .

/iaw



回答4:

You can use node.js and tar-to-zip for this purpose. All you need to do is:

Install node.js with nvm if you do not have it.

And then install tar-to-zip with:

npm i tar-to-zip -g

And use it with:

tarzip *.tar.gz

Also you can convert .tar.gz files to .zip programmatically. You should install async and tar-to-zip locally:

npm i async tar-to-zip

And then create converter.js with contents:

#!/usr/bin/env node

'use strict';

const fs = require('fs');
const tarToZip = require('tar-to-zip');
const eachSeries = require('async/eachSeries');
const names = process.argv.slice(2);

eachSeries(names, convert, exitIfError);

function convert(name, done) {
    const {stdout} = process;
    const onProgress = (n) => {
        stdout.write(`\r${n}%: ${name}`);
    };
    const onFinish = (e) => {
        stdout.write('\n');
        done();
    };

    const nameZip = name.replace(/\.tar\.gz$/, '.zip');    
    const zip = fs.createWriteStream(nameZip)
        .on('error', (error) => {
            exitIfError(error);
            fs.unlinkSync(zipPath);
        });

    const progress = true;
    tarToZip(name, {progress})
        .on('progress', onProgress)
        .on('error', exitIfError)
        .getStream()
        .pipe(zip)
        .on('finish', onFinish);
}

function exitIfError(error) {
    if (!error)
        return;

    console.error(error.message);
    process.exit(1);
}


回答5:

Zipfiles are handy because they offer random access to files. Tar files only sequential.

My solution for this conversion is this shell script, which calls itself via tar(1) "--to-command" option. (I prefer that rather than having 2 scripts). But I admit "untar and zip -r" is faster than this, because zipnote(1) cannot work in-place, unfortunately.

#!/bin/zsh -feu

## Convert a tar file into zip:

usage() {
    setopt POSIX_ARGZERO
    cat <<EOF
    usage: ${0##*/} [+-h] [-v] [--] {tarfile} {zipfile}"

-v verbose
-h print this message
converts the TAR archive into ZIP archive.
EOF
    unsetopt POSIX_ARGZERO
}

while getopts :hv OPT; do
    case $OPT in
        h|+h)
            usage
            exit
            ;;
        v)
            # todo: ignore TAR_VERBOSE from env?
            # Pass to the grand-child process:
            export TAR_VERBOSE=y
            ;;
        *)
            usage >&2
            exit 2
    esac
done
shift OPTIND-1
OPTIND=1

# when invoked w/o parameters:
if [ $# = 0 ] # todo: or stdin is not terminal
then
    # we are invoked by tar(1)
    if [ -n "${TAR_VERBOSE-}" ]; then echo $TAR_REALNAME >&2;fi
    zip --grow --quiet $ZIPFILE -
    # And rename it:
    # fixme: this still makes a full copy, so slow.
    printf "@ -\n@=$TAR_REALNAME\n" | zipnote -w $ZIPFILE
else
    if [ $# != 2 ]; then usage >&2; exit 1;fi
    # possibly: rm -f $ZIPFILE
    ZIPFILE=$2 tar -xaf $1 --to-command=$0
fi


回答6:

Here is a python solution based on this answer here:

import sys, tarfile, zipfile, glob

def convert_one_archive(file_name):
    out_file = file_name.replace('.tar.gz', '.zip')
    with tarfile.open(file_name, mode='r:gz') as tf:
        with zipfile.ZipFile(out_file, mode='a', compression=zipfile.ZIP_DEFLATED) as zf:
            for m in tf.getmembers():
                f = tf.extractfile( m )
                fl = f.read()
                fn = m.name
                zf.writestr(fn, fl)

for f in glob.glob('*.tar.gz'):
    convert_one_archive(f)