Skip to content

Progress and Records

A loop tells you nothing until it ends. Parallel can narrate a run while it happens, and write down what it did so the run can be audited — or resumed — afterwards. Everything here was run for real in transcripts t04, t04b, t09 and t09b.

While it runs: --bar, --eta, --progress

--bar draws a progress bar on the terminal that redraws itself as jobs complete:

$ parallel --bar sleep 0.4 ::: 1 2 3 4 5 6 7 8 9 10 11 12
100% 12:0=44s 12

(the elapsed figure is this capture's own truth — the lab machine was under heavy load; transcript t04b explains). --progress prints a plainer text summary as it goes, and --eta adds an estimate of the time remaining. All three write to standard error, so they never pollute output you are piping somewhere.

Afterwards: the job log

--joblog FILE writes one line per job:

$ ls samples/*/*.txt | parallel --joblog jobs.log sha256sum {} > checksums.txt
$ head -3 jobs.log
Seq Host    Starttime   JobRuntime  Send    Receive Exitval Signal  Command
1   :   1792888022.580       0.042  0   93  0   0   sha256sum samples/north/notes-01.txt
2   :   1792888022.603       0.060  0   93  0   0   sha256sum samples/north/notes-02.txt

The columns that matter day to day: Seq (the job's number), Exitval (0 means success — anything else is the command's failure code) and JobRuntime in seconds. Because the log is plain tab-separated text, the usual tools read it. From transcript t04, the total work across twelve checksum jobs:

$ awk "NR>1 {t+=$4} END {print \"total job time:\", t, \"seconds across\", NR-1, \"jobs\"}" jobs.log
total job time: 0.702 seconds across 12 jobs

0.702 seconds of job time in all — the jobs are quick; the log is how you can prove it afterwards instead of remembering it.

Every output, filed: --results

--results DIR saves each job's standard output and standard error to its own files:

$ parallel --results outdir/ echo processed {} ::: samples/north/notes-01.txt samples/south/notes-08.txt
$ find outdir -type f | sort
outdir/1/samples+znorth+znotes-01.txt/seq
outdir/1/samples+znorth+znotes-01.txt/stderr
outdir/1/samples+znorth+znotes-01.txt/stdout
outdir/1/samples+zsouth+znotes-08.txt/seq
outdir/1/samples+zsouth+znotes-08.txt/stderr
outdir/1/samples+zsouth+znotes-08.txt/stdout

The path is the input with its slashes rewritten (/ becomes +z), under a folder per input source. After a five-hundred-job run, this is how you find the one job that complained without scrolling a terminal.

Picking up after failure

The job log is also the resume ticket. Two different continuations exist, and transcript t09 keeps them straight:

  • --resume --joblog FILE — for a run that was interrupted. Jobs already in the log are skipped, the rest of the list runs. (In the lab's finished test run every job was in the log, so --resume ran nothing and exited 0.)
  • --retry-failed --joblog FILE — for a run that finished with failures. Only the jobs whose Exitval was not 0 run again:
$ parallel --joblog resume.log 'echo ran {} >> ran.txt; test -f {}' ::: present-a absent present-b
parallel exit: 1
$ touch absent
$ parallel --retry-failed --joblog resume.log 'echo ran {} >> ran.txt; test -f {}' ::: present-a absent present-b
parallel exit: 0
$ cat ran.txt
ran present-a
ran absent
ran present-b
ran absent

absent ran twice — once failing, once after the fix — and nothing else repeated. Fix the cause, re-run the failures, leave the successes alone: that is the whole workflow, and it is the strongest reason to give every serious run a job log.