Compare commits

..

3 commits

Author SHA1 Message Date
xenofem 105839d8cf add README 2021-11-22 20:44:15 -05:00
xenofem 918b3bf10b parse float values 2021-11-22 20:40:53 -05:00
xenofem 6fec8076b4 add window size flag, tweak flag documentation 2021-11-22 20:40:19 -05:00
2 changed files with 61 additions and 8 deletions

54
README.md Normal file
View file

@ -0,0 +1,54 @@
# impatient
A general purpose utility for estimating when a task will complete
## Overview
`impatient` is sort of like `pv` for tasks where you can't insert
extra commands into a pipeline, and sort of like `progress` for tasks
that aren't reading/writing a specific file on the local system. If
you give it any shell command that outputs a number (possibly with a
K/M/G/T suffix), it will repeatedly run the command and show you how
fast that number is changing. If you give it the expected final value
of the number, it'll also estimate how much longer you have to wait.
It also has a specific option for tracking the total size of a
file or directory as given by `du`.
For example, here's `impatient` tracking an in-progress `zfs send` to
a remote machine:
```
$ impatient -f "$(zfs list -pH -o used pool/home)" -c 'ssh remote-server zfs list -pH -o used remote-pool/backups/pool/home' -i 60
109.8G - 2.8M/s - 263.7G total - 41.6% complete - 15h03m remaining - ETA 2021-11-23 11:34
```
Note that `-f` is given the value output by `zfs list`; we could
give it a number directly, this just simplifies things in this
particular scenario. The `-p` flag for `zfs list` isn't strictly
necessary, `impatient` can parse values like `50.7G`, but
using the exact byte value provides more precision.
`-c`, on the other hand, is given a quoted string containing a
command, which will output the current progress value each time it's
run. The command given to `-c` can be an arbitrary shell one-liner,
use responsibly.
## Usage
```
usage: impatient [-h] [-f FINAL] [-i INTERVAL] [-w WINDOW] (-p PATH | -c COMMAND)
Display progress and time estimates for arbitrary tasks
optional arguments:
-h, --help show this help message and exit
-f FINAL, --final FINAL
Expected final size/value at completion, optionally with a K/M/G/T suffix
-i INTERVAL, --interval INTERVAL
Interval in seconds between samples (default 10)
-w WINDOW, --window WINDOW
Number of samples to keep for the sliding window (default 100)
-p PATH, --path PATH Track total disk usage of a given path
-c COMMAND, --command COMMAND
Track value returned by a shell command; this should return a single number, optionally with a K/M/G/T suffix
```

View file

@ -6,16 +6,15 @@ import subprocess
import sys import sys
import time import time
WINDOW_SIZE = 100
SUFFIXES = 'KMGT' SUFFIXES = 'KMGT'
parser = argparse.ArgumentParser(description='Display progress and time estimates for arbitrary tasks') parser = argparse.ArgumentParser(description='Display progress and time estimates for arbitrary tasks')
parser.add_argument('-f', '--final', type=str, help='Expected final size/value at completion.') parser.add_argument('-f', '--final', type=str, help='Expected final size/value at completion, optionally with a K/M/G/T suffix')
parser.add_argument('-i', '--interval', type=int, default=10, help='Interval in seconds between samples (default 10)') parser.add_argument('-i', '--interval', type=int, default=10, help='Interval in seconds between samples (default 10)')
parser.add_argument('-w', '--window', type=int, default=100, help='Number of samples to keep for the sliding window (default 100)')
tracker_types = parser.add_mutually_exclusive_group(required=True) tracker_types = parser.add_mutually_exclusive_group(required=True)
tracker_types.add_argument('-p', '--path', type=str, help='Track total disk usage of a given path') tracker_types.add_argument('-p', '--path', type=str, help='Track total disk usage of a given path')
tracker_types.add_argument('-c', '--command', type=str, help='Track value returned by a shell command; this should return a single number, optionally followed by K/M/G/T') tracker_types.add_argument('-c', '--command', type=str, help='Track value returned by a shell command; this should return a single number, optionally with a K/M/G/T suffix')
args = parser.parse_args() args = parser.parse_args()
@ -23,9 +22,9 @@ def parse_value(v):
suffix = v[-1].upper() suffix = v[-1].upper()
if suffix in SUFFIXES: if suffix in SUFFIXES:
exponent = 3*(SUFFIXES.find(suffix)+1) exponent = 3*(SUFFIXES.find(suffix)+1)
return int(v[:-1])*(10**exponent) return float(v[:-1])*(10**exponent)
else: else:
return int(v) return float(v)
def display_value(v): def display_value(v):
suffix = '' suffix = ''
@ -67,8 +66,8 @@ while True:
samples.append(current) samples.append(current)
if len(samples) < 2: if len(samples) < 2:
continue continue
if len(samples) > WINDOW_SIZE: if len(samples) > args.window:
samples = samples[-WINDOW_SIZE:] samples = samples[-args.window:]
rate = (current - samples[0])/((len(samples)-1)*args.interval) rate = (current - samples[0])/((len(samples)-1)*args.interval)