6

Is it possible to extract a tar.xz package in golang? My understanding is it's possible to use the library for tar and sending it to an xz go library.

Fire
  • 306
  • 1
  • 4
  • 10

3 Answers3

6

I recently created an XZ decompression package so it is now possible to extract a tar.xz using only Go code.

The following code extracts the file myfile.tar.xz to the current directory:

package main

import (
    "archive/tar"
    "fmt"
    "io"
    "log"
    "os"

    "github.com/xi2/xz"
)

func main() {
    // Open a file
    f, err := os.Open("myfile.tar.xz")
    if err != nil {
        log.Fatal(err)
    }
    // Create an xz Reader
    r, err := xz.NewReader(f, 0)
    if err != nil {
        log.Fatal(err)
    }
    // Create a tar Reader
    tr := tar.NewReader(r)
    // Iterate through the files in the archive.
    for {
        hdr, err := tr.Next()
        if err == io.EOF {
            // end of tar archive
            break
        }
        if err != nil {
            log.Fatal(err)
        }
        switch hdr.Typeflag {
        case tar.TypeDir:
            // create a directory
            fmt.Println("creating:   " + hdr.Name)
            err = os.MkdirAll(hdr.Name, 0777)
            if err != nil {
                log.Fatal(err)
            }
        case tar.TypeReg, tar.TypeRegA:
            // write a file
            fmt.Println("extracting: " + hdr.Name)
            w, err := os.Create(hdr.Name)
            if err != nil {
                log.Fatal(err)
            }
            _, err = io.Copy(w, tr)
            if err != nil {
                log.Fatal(err)
            }
            w.Close()
        }
    }
    f.Close()
}
Michael Hampton
  • 9,737
  • 4
  • 55
  • 96
xi2
  • 96
  • 1
  • 2
4

http://golang.org/pkg/archive/tar/#example_

also you can do

import "os/exec"

cmd := exec.Command("tar", "-x", "/your/archive.tar.xz")
err := cmd.Run()
Uvelichitel
  • 8,220
  • 1
  • 19
  • 36
  • This doesn't actually decompress via tar. You probably want tar xvJf archive.tar.xz... – Fire Feb 01 '15 at 07:08
  • @Fire It does with GNU tar, as GNU tar automatically detects the presence of compression. None the less, to keep this program compatible with other tar implementations that do not provide `J`, you might want to set up a pipe between `xz -d` and `tar x`. Also, why do you provide `v`? It isn't required. – fuz Feb 02 '15 at 07:30
  • 1
    That's true. `v` isn't required. – Fire Feb 02 '15 at 17:00
1

There is no Lempel-Ziv-Markow encoder or decoder in the Go standard library. If you are allowed to assume that the platform your code runs on provides the xz utility, you could use stub functions like these:

import "os/exec"

// decompress xz compressed data stream r.
func UnxzReader(r io.Reader) (io.ReadCloser, error) {
    unxz := exec.Command("xz", "-d")
    unxz.Stdin = r
    out, err := unxz.StdoutPipe()
    if err != nil {
        return nil, err
    }

    err = unxz.Start()
    if err != nil {
        return nil, err
    }

    // we are not interested in the exit status, but we should really collect
    // that zombie process
    go unxz.Wait()

    return out, nil
}
fuz
  • 88,405
  • 25
  • 200
  • 352
  • Note not enough arguments to return. – Fire Feb 02 '15 at 05:30
  • @Fire Sorry, I was tired when I wrote this. All bugs are fixed by now. – fuz Feb 02 '15 at 07:28
  • I have code that processes the tar, but it is also http://golang.org/pkg/archive/tar/#example here. It would be best for completeness to have code that starts from an os.Open() and decompresses xz using the above code. Afterwards, send it to tar and for each directory / file extract. – Fire Feb 02 '15 at 16:48
  • It would be best for future searches I mean. – Fire Feb 02 '15 at 17:03
  • @Fire The rest is left as an exercise to the reader. It's not really difficult. – fuz Feb 02 '15 at 17:16
  • For future readers, note that `xz -d -T0` uses all the threads of the computer to decompress. – Fire Feb 02 '15 at 22:53