158 lines
3.8 KiB
Go
158 lines
3.8 KiB
Go
// packer - a simple CLI tool to find and zip file corresponding to a given regex
|
|
// find + zip linux CLI works too
|
|
package main
|
|
|
|
import (
|
|
"archive/zip"
|
|
"fmt"
|
|
"github.com/akamensky/argparse"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
fp "path/filepath"
|
|
re "regexp"
|
|
)
|
|
|
|
// parseArguments - get needed arguments to launch the `packer`
|
|
// returns 3 strings :
|
|
// * regex
|
|
// * outputPath
|
|
// * directory
|
|
func parseArguments() (string, string, string, error) {
|
|
parser := argparse.NewParser("packer", "packer - zip file where filenames match with a regex")
|
|
var err error
|
|
|
|
regex := parser.String("r", "regex", &argparse.Options{Required: true, Help: "filename Regex"})
|
|
outputPath := parser.String("o", "output-path", &argparse.Options{Required: true, Help: "zip output path (including zip name)"})
|
|
directory := parser.String("d", "directory", &argparse.Options{Required: false, Help: "root directory to check files", Default: "."})
|
|
|
|
parseError := parser.Parse(os.Args)
|
|
if parseError != nil {
|
|
err = fmt.Errorf("parsing error: %s", parseError)
|
|
}
|
|
|
|
return *regex, *directory, *outputPath, err
|
|
}
|
|
|
|
// buildRegex - build POSIX regex from a string
|
|
// WARN: lookahead and lookbehind are not supported
|
|
func buildRegex(regex string) (*re.Regexp, error) {
|
|
rc, err := re.CompilePOSIX(regex)
|
|
return rc, err
|
|
}
|
|
|
|
// walkFilteredDir - walk into a directory and its subdirectories and find files that match a regex
|
|
func walkDirFiltered(directory string, regex re.Regexp) ([]string, error) {
|
|
var files []string
|
|
err := fp.Walk(directory,
|
|
func(path string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !info.IsDir() && regex.MatchString(path) {
|
|
log.Println("file found :", path)
|
|
files = append(files, path)
|
|
}
|
|
return err
|
|
})
|
|
return files, err
|
|
}
|
|
|
|
// getFilesFromDirectoryPath - wrapper calling `walkDirFiltered()`
|
|
func getFilesFromDirectoryPath(directory string, regex re.Regexp) ([]string, error) {
|
|
files, err := walkDirFiltered(directory, regex)
|
|
return files, err
|
|
}
|
|
|
|
// writeFileContent - write file content into zipped file
|
|
// chunking is used to avoid memory errors with large files
|
|
func writeFileContent(f *os.File, zf io.Writer) (err error) {
|
|
const chunkSize = 4
|
|
b := make([]byte, chunkSize)
|
|
|
|
for {
|
|
_, err := f.Read(b)
|
|
if err != nil {
|
|
if err != io.EOF {
|
|
continue
|
|
}
|
|
break
|
|
}
|
|
zf.Write(b)
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
// generateZip - create a zip archive
|
|
// * outputPath: zip archive full path
|
|
// * filePaths: files path
|
|
func generateZip(outputPath string, filepaths []string) (err error) {
|
|
|
|
// create a zipfile from `outputPath`
|
|
zipPath, err := os.Create(outputPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer zipPath.Close()
|
|
|
|
// create a new zip archive from `zipPath`
|
|
w := zip.NewWriter(zipPath)
|
|
|
|
for _, filepath := range filepaths {
|
|
// open matching regex file
|
|
f, err := os.Open(filepath)
|
|
if err != nil {
|
|
log.Println("[ERROR] :", err)
|
|
continue
|
|
}
|
|
|
|
// create a zipped file
|
|
zf, err := w.Create(filepath)
|
|
if err != nil {
|
|
log.Println("[ERROR] :", err)
|
|
f.Close()
|
|
continue
|
|
}
|
|
|
|
// write file content into zipped file
|
|
err = writeFileContent(f, zf)
|
|
|
|
// clean up opened file
|
|
f.Close()
|
|
}
|
|
|
|
// make sure to check the error on Close.
|
|
err = w.Close()
|
|
|
|
return err
|
|
}
|
|
|
|
func main() {
|
|
|
|
// parse arguments
|
|
regex, directory, outputPath, err := parseArguments()
|
|
if err != nil {
|
|
log.Fatal("error while parsing args : ", err)
|
|
}
|
|
|
|
// build regex from string
|
|
rc, err := buildRegex(regex)
|
|
if err != nil {
|
|
log.Fatal("error while compiling regex : '", regex, "'\nWARN: lookahead and lookbehind not implemented")
|
|
}
|
|
|
|
// deferencing Regexp pointer
|
|
rd := *rc
|
|
|
|
// walk into directory
|
|
log.Printf("looking for file '%s' in '%s' ...", rd.String(), directory)
|
|
files, err := getFilesFromDirectoryPath(directory, rd)
|
|
|
|
// generating zip archive
|
|
log.Printf("files found : %d", len(files))
|
|
generateZip(outputPath, files)
|
|
|
|
log.Printf("zip archive generated in %s", outputPath)
|
|
}
|