Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Reading through a tar.gz file in Go / golang
package main
import (
"archive/tar"
"compress/gzip"
"flag"
"fmt"
"io"
"os"
)
func main() {
// get the arguments from the command line
numPtr := flag.Int("n", 4, "an integer")
flag.Parse()
sourceFile := flag.Arg(0)
if sourceFile == "" {
fmt.Println("Dude, you didn't pass in a tar file!")
os.Exit(1)
}
fmt.Println("arg 1: ", flag.Arg(0))
processFile(sourceFile, *numPtr)
}
func processFile(srcFile string, num int) {
f, err := os.Open(srcFile)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer f.Close()
gzf, err := gzip.NewReader(f)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
tarReader := tar.NewReader(gzf)
i := 0
for {
header, err := tarReader.Next()
if err == io.EOF {
break
}
if err != nil {
fmt.Println(err)
os.Exit(1)
}
name := header.Name
switch header.Typeflag {
case tar.TypeDir:
continue
case tar.TypeReg:
fmt.Println("(", i, ")", "Name: ", name)
if i == num {
fmt.Println(" --- ")
io.Copy(os.Stdout, tarReader)
fmt.Println(" --- ")
os.Exit(0)
}
default:
fmt.Printf("%s : %c %s %s\n",
"Yikes! Unable to figure out type",
header.Typeflag,
"in file",
name,
)
}
i++
}
}
@surajnarwade
Copy link

How to write unit test for the same ?

@dkartachov
Copy link

dkartachov commented Oct 8, 2022

I am working with symbolic links so I took the liberty to add a case for those to @joonas-fi's solution. Simple addition, but it works and I hope it helps someone :)

package tarextract

// hat tip https://gist.github.com/indraniel/1a91458984179ab4cf80

import (
	"archive/tar"
	"compress/gzip"
	"io"
	"log"
	"os"
)

func ExtractTarGz(gzipStream io.Reader) {
	uncompressedStream, err := gzip.NewReader(gzipStream)
	if err != nil {
		log.Fatal("ExtractTarGz: NewReader failed")
	}

	tarReader := tar.NewReader(uncompressedStream)

	for true {
		header, err := tarReader.Next()

		if err == io.EOF {
			break
		}

		if err != nil {
			log.Fatalf("ExtractTarGz: Next() failed: %s", err.Error())
		}

		switch header.Typeflag {
		case tar.TypeDir:
			if err := os.Mkdir(header.Name, 0755); err != nil {
				log.Fatalf("ExtractTarGz: Mkdir() failed: %s", err.Error())
			}
		case tar.TypeReg:
			outFile, err := os.Create(header.Name)
			if err != nil {
				log.Fatalf("ExtractTarGz: Create() failed: %s", err.Error())
			}
			defer outFile.Close()
			if _, err := io.Copy(outFile, tarReader); err != nil {
				log.Fatalf("ExtractTarGz: Copy() failed: %s", err.Error())
			}
                case tar.TypeSymlink:
                        os.Symlink(header.Linkname, header.Name)
		default:
			log.Fatalf(
				"ExtractTarGz: uknown type: %s in %s",
				header.Typeflag,
				header.Name)
		}
	}
}

@joonas-fi
Copy link

Thanks for sharing @dkartachov, have a nice day :)

@indraniel
Copy link
Author

I echo @joonas-fi, thanks for the improvements @dkartachov !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment