summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRose Hogenson <rosehogenson@posteo.net>2025-04-05 08:03:18 -0700
committerRose Hogenson <rosehogenson@posteo.net>2025-04-05 08:03:18 -0700
commit2fec8a20fb8f485f2107deb00d29c99dfdfbde47 (patch)
treedaf28f9b60a4af3266754fc92aeb8eb6f77d7426
downloadprogress-bar-2fec8a20fb8f485f2107deb00d29c99dfdfbde47.tar.zst
Initial commit
-rw-r--r--.gitignore1
-rw-r--r--go.mod7
-rw-r--r--go.sum4
-rw-r--r--progress-bar.go76
4 files changed, 88 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b5b2c1b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/progress-bar
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..8d19cf8
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,7 @@
+module gitlab.com/rhogenson/progress-bar
+
+go 1.24.1
+
+require golang.org/x/term v0.28.0
+
+require golang.org/x/sys v0.29.0 // indirect
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..f05f63b
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,4 @@
+golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
+golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
+golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
diff --git a/progress-bar.go b/progress-bar.go
new file mode 100644
index 0000000..0b1cefe
--- /dev/null
+++ b/progress-bar.go
@@ -0,0 +1,76 @@
+package main
+
+import (
+ "bytes"
+ "errors"
+ "flag"
+ "fmt"
+ "os"
+ "os/exec"
+ "strconv"
+ "strings"
+ "time"
+
+ "golang.org/x/term"
+)
+
+var n = flag.Duration("n", 2*time.Minute, "polling interval")
+
+func run() error {
+ cols, _, err := term.GetSize(int(os.Stdout.Fd()))
+ if err != nil {
+ return err
+ }
+ args := flag.Args()
+ if len(args) < 2 {
+ return errors.New("usage error")
+ }
+ maxStr := args[0]
+ maxVal, err := strconv.Atoi(maxStr)
+ if err != nil {
+ return fmt.Errorf("max: %s", err)
+ }
+ cmd := args[1:]
+ type measurement struct {
+ t time.Time
+ i int
+ }
+ var measurements []measurement
+ for i := 0; ; i++ {
+ outputBytes, err := exec.Command(cmd[0], cmd[1:]...).Output()
+ if err != nil {
+ fmt.Println(err)
+ continue
+ }
+ output, err := strconv.Atoi(string(bytes.TrimSuffix(outputBytes, []byte("\n"))))
+ if err != nil {
+ fmt.Println(err)
+ continue
+ }
+ p := output * cols / maxVal
+ if i > 0 {
+ fmt.Print("\033[2F\033[J")
+ }
+ fmt.Printf("%s>\n", strings.Repeat("=", max(p-1, 0)))
+ measurements = append(measurements, measurement{time.Now(), output})
+ if len(measurements) > 20 {
+ measurements = measurements[1:]
+ }
+ deltaT := measurements[len(measurements)-1].t.Sub(measurements[0].t)
+ delta := measurements[len(measurements)-1].i - measurements[0].i
+ if delta == 0 {
+ fmt.Println()
+ } else {
+ fmt.Printf("ETA: %s\n", time.Duration(float64(maxVal-output)/float64(delta)*float64(deltaT)).Round(time.Second))
+ }
+ time.Sleep(*n)
+ }
+}
+
+func main() {
+ flag.Parse()
+ if err := run(); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+}