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) } }