xargs -P 8 is most of what I need from parallel, minus the progress indicator. Adding one is not hard if you accept that you need a counter in a separate process. I use a FIFO and a tiny awk consumer.

#!/usr/bin/env bash
set -euo pipefail

jobs=()
while IFS= read -r line; do jobs+=("$line"); done
total=${#jobs[@]}

fifo="$(mktemp -u)"
mkfifo "$fifo"
trap 'rm -f "$fifo"' EXIT

# Background counter: reads one line per completed job.
(
  awk -v total="$total" '
    { i++; printf "\r  %d / %d done", i, total > "/dev/stderr" }
    END { print "" > "/dev/stderr" }
  ' < "$fifo"
) &

printf '%s\n' "${jobs[@]}" | \
  xargs -P 8 -I {} bash -c '
    if output=$("$0" "$1" 2>&1); then
      printf "ok\n" > "$2"
    else
      printf "ok\n" > "$2"
      printf "FAIL %s\n%s\n" "$1" "$output" >&2
    fi
  ' my_worker.sh {} "$fifo"

wait

The subshell writes “ok” to the FIFO when a job finishes (success or failure). The awk consumer increments a counter and redraws the line. Progress stays on stderr so the stdout pipe is unaffected. The FIFO closes automatically when xargs is done.

See also /snippets/bash-trap-cleanup-tmpdir/.