Bash Brace Expansion in Go
Go implementation of Brace Expansion mechanism to generate arbitrary strings.
Implementing: 3.5.1 Brace Expansion
- Usage
- Get string slice
- Generating directory tree
- Expand URLS
- Need error checking?
- With os.Expand
- With os.ExpandEnv
Usage
go get github.com/mkungla/bexp/v3
Get string slice
package main
import (
"fmt"
"github.com/mkungla/bexp/v3"
)
func main() {
var v []string
v = bexp.Parse("file-{a,b,c}.jpg")
fmt.Println(v)
// [file-a.jpg file-b.jpg file-c.jpg]
v = bexp.Parse("-v{,,}")
fmt.Println(v)
// [-v -v -v]
v = bexp.Parse("file{0..2}.jpg")
fmt.Println(v)
// [file0.jpg file1.jpg file2.jpg]
v = bexp.Parse("file{2..0}.jpg")
fmt.Println(v)
// [file2.jpg file1.jpg file0.jpg]
v = bexp.Parse("file{0..4..2}.jpg")
fmt.Println(v)
// [file0.jpg file2.jpg file4.jpg]
v = bexp.Parse("file-{a..e..2}.jpg")
fmt.Println(v)
// [file-a.jpg file-c.jpg file-e.jpg]
v = bexp.Parse("file{00..10..5}.jpg")
fmt.Println(v)
// [file00.jpg file05.jpg file10.jpg]
v = bexp.Parse("{{A..C},{a..c}}")
fmt.Println(v)
// [A B C a b c]
v = bexp.Parse("ppp{,config,oe{,conf}}")
fmt.Println(v)
// [ppp pppconfig pppoe pppoeconf]
v = bexp.Parse("data/{P1/{10..19},P2/{20..29},P3/{30..39}}")
fmt.Println(v)
// [data/P1/10 data/P1/11 data/P1/12 data/P1/13 data/P1/14 data/P1/15 data/P1/16 data/P1/17 data/P1/18 data/P1/19 data/P2/20 data/P2/21 data/P2/22 data/P2/23 data/P2/24 data/P2/25 data/P2/26 data/P2/27 data/P2/28 data/P2/29 data/P3/30 data/P3/31 data/P3/32 data/P3/33 data/P3/34 data/P3/35 data/P3/36 data/P3/37 data/P3/38 data/P3/39]
}
Generating directory tree
package main
import (
"log"
"github.com/mkungla/bexp/v3"
)
func main() {
const (
rootdir = "/tmp/bexp"
treeexp = rootdir + "/{dir1,dir2,dir3/{subdir1,subdir2}}"
)
if err := bexp.MkdirAll(treeexp, 0750); err != nil {
log.Fatal(err)
}
// Will produce directory tree
// /tmp/bexp
// /tmp/bexp/dir1
// /tmp/bexp/dir2
// /tmp/bexp/dir3
// /tmp/bexp/dir3/subdir1
// /tmp/bexp/dir3/subdir2
}
Expand URLS
The example shows how to create Openstreetmap tiles
around the desired latitude and longitude coordinates.
package main
import (
"fmt"
"log"
"math"
"github.com/mkungla/bexp/v3"
)
func getCenterTile(lat, long float64, zoom int) (z, x, y int) {
n := math.Exp2(float64(zoom))
x = int(math.Floor((long + 180.0) / 360.0 * n))
if float64(x) >= n {
x = int(n - 1)
}
y = int(math.Floor((1.0 - math.Log(
math.Tan(lat*math.Pi/180.0)+
1.0/math.Cos(lat*math.Pi/180.0))/
math.Pi) / 2.0 * n))
return x, y, zoom
}
func main() {
x, y, z := getCenterTile(51.03, 13.78, 5)
pattern := fmt.Sprintf(
"https://tile.openstreetmap.org/%d/{%d..%d}/{%d..%d}.png",
z, x-2, x+2, y-2, y+2,
)
tiles := bexp.Parse(pattern)
fmt.Println("pattern: ", pattern)
for _, tile := range tiles {
fmt.Println(tile)
}
// Output:
// pattern: https://tile.openstreetmap.org/5/{15..19}/{8..12}.png
// https://tile.openstreetmap.org/5/15/8.png
// https://tile.openstreetmap.org/5/15/9.png
// https://tile.openstreetmap.org/5/15/10.png
// https://tile.openstreetmap.org/5/15/11.png
// https://tile.openstreetmap.org/5/15/12.png
// https://tile.openstreetmap.org/5/16/8.png
// https://tile.openstreetmap.org/5/16/9.png
// https://tile.openstreetmap.org/5/16/10.png
// https://tile.openstreetmap.org/5/16/11.png
// https://tile.openstreetmap.org/5/16/12.png
// https://tile.openstreetmap.org/5/17/8.png
// https://tile.openstreetmap.org/5/17/9.png
// https://tile.openstreetmap.org/5/17/10.png
// https://tile.openstreetmap.org/5/17/11.png
// https://tile.openstreetmap.org/5/17/12.png
// https://tile.openstreetmap.org/5/18/8.png
// https://tile.openstreetmap.org/5/18/9.png
// https://tile.openstreetmap.org/5/18/10.png
// https://tile.openstreetmap.org/5/18/11.png
// https://tile.openstreetmap.org/5/18/12.png
// https://tile.openstreetmap.org/5/19/8.png
// https://tile.openstreetmap.org/5/19/9.png
// https://tile.openstreetmap.org/5/19/10.png
// https://tile.openstreetmap.org/5/19/11.png
// https://tile.openstreetmap.org/5/19/12.png
}
Need error checking?
package main
import (
"errors"
"fmt"
"github.com/mkungla/bexp/v3"
)
func main() {
empty, err := bexp.ParseValid("")
fmt.Printf("%q - %t\n", empty[0], errors.Is(err, bexp.ErrEmptyResult))
abc, err := bexp.ParseValid("abc")
fmt.Printf("%q - %t\n", abc[0], errors.Is(err, bexp.ErrUnchangedBraceExpansion))
// Output:
// "" - true
// "abc" - true
}
With os.Expand
package main
import (
"fmt"
"os"
"github.com/mkungla/bexp/v3"
)
func main() {
const treeExp = "$MY_ROOT_DIR/dir{1..3}/{subdir1,subdir2}"
mapper := func(varName string) string {
switch varName {
case "MY_ROOT_DIR":
return "/my_root"
}
return ""
}
str := os.Expand(treeExp, mapper)
fmt.Println("str := os.Expand(treeExp, mapper)")
fmt.Println(str)
fmt.Println("v := bexp.Parse(str)")
v := bexp.Parse(str)
for _, p := range v {
fmt.Println(p)
}
// Output:
// str := os.Expand(treeExp, mapper)
// /my_root/dir{1..3}/{subdir1,subdir2}
// v := bexp.Parse(str)
// /my_root/dir1/subdir1
// /my_root/dir1/subdir2
// /my_root/dir2/subdir1
// /my_root/dir2/subdir2
// /my_root/dir3/subdir1
// /my_root/dir3/subdir2
}
With os.ExpandEnv
package main
import (
"fmt"
"os"
"github.com/mkungla/bexp/v3"
)
func main() {
const treeExp = "$MY_ROOT_DIR/dir{1..3}/{subdir1,subdir2}"
os.Setenv("MY_ROOT_DIR", "/my_root")
str := os.ExpandEnv(treeExp)
fmt.Println("str := os.ExpandEnv(treeExp)")
fmt.Println(str)
fmt.Println("v := bexp.Parse(str)")
v := bexp.Parse(str)
for _, p := range v {
fmt.Println(p)
}
// Output:
// str := os.ExpandEnv(treeExp)
// /my_root/dir{1..3}/{subdir1,subdir2}
// v := bexp.Parse(str)
// /my_root/dir1/subdir1
// /my_root/dir1/subdir2
// /my_root/dir2/subdir1
// /my_root/dir2/subdir2
// /my_root/dir3/subdir1
// /my_root/dir3/subdir2
}
Inspired by and other similar libraries
following package were inspiration to create this package, The motivation of this package is to improve performance and reduce memory allocations compared to other solutions. Also to add some commonly used API's when working with brace expansion strings
- @kujtimiihoxha go-brace-expansion Go bash style brace expansion
- @thomasheller braceexpansion Shell brace expansion implemented in Go (golang).
- @pittfit ortho Go brace expansion library
- @kujtimiihoxha go-balanced-match Go balanced match