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)