Model downloads, fine-tunes, big rsyncs — anything that outlives an SSH session. park drops the command into a detached tmux session and tees its output to a log; parked tells you which ones are still going, finished, or died.
~/.local/bin/park — start a command detached, log it, auto-close on finish:
#!/usr/bin/env bash
# usage: park <session-name> <command...>
set -euo pipefail
if [ "$#" -lt 2 ]; then
echo "usage: park <session-name> <command...>" >&2
exit 1
fi
session="$1"; shift
# refuse to clobber a session that's already running
if tmux has-session -t "$session" 2>/dev/null; then
echo "session '$session' already exists — attach with: tmux attach -t $session" >&2
exit 1
fi
logdir="$HOME/.park"
mkdir -p "$logdir"
logfile="$logdir/${session}.log"
# -d = detached: tmux runs the command in the background and returns immediately
tmux new-session -d -s "$session" \
bash -c '
set -o pipefail
logfile="$1"; shift
{
printf ">>> started: %s\n" "$(date)"
printf ">>> cmd:"; printf " %q" "$@"; printf "\n\n" # %q re-quotes each arg exactly
"$@"
} 2>&1 | tee "$logfile"
code=${PIPESTATUS[0]} # real exit code, not tee's
{
printf "\n>>> exit code: %s\n" "$code"
printf ">>> finished: %s\n" "$(date)"
} >> "$logfile"
' bash "$logfile" "$@"
echo "started '$session' (log: $logfile)"
echo "check later: tmux ls | tail -f $logfile | cat $logfile"
~/.local/bin/parked — summarize every task at a glance, or clear finished ones:
#!/usr/bin/env bash
# usage:
# parked [-l|--logs] list tasks (-l also prints each log's last line)
# parked -k|--cancel <name> stop a running task (kills its tmux session)
# parked -c|--clear [ok|failed|all] remove logs for finished tasks (default: all = ok + failed)
set -euo pipefail
logdir="$HOME/.park"
mode=list
show_tail=0
clear_what=all
target=
case "${1:-}" in
-l|--logs) show_tail=1 ;;
-k|--cancel)
mode=cancel
target="${2:-}"
if [ -z "$target" ]; then echo "usage: parked -k|--cancel <name>" >&2; exit 1; fi ;;
-c|--clear)
mode=clear
clear_what="${2:-all}"
case "$clear_what" in
ok|failed|all) ;;
*) echo "usage: parked -c|--clear [ok|failed|all]" >&2; exit 1 ;;
esac ;;
"") ;;
*) echo "usage: parked [-l|--logs] | parked -k|--cancel <name> | parked -c|--clear [ok|failed|all]" >&2; exit 1 ;;
esac
# colours only in list mode, and only when writing to a real terminal
if [ "$mode" = list ] && [ -t 1 ]; then
c_run=$(tput setaf 4); c_ok=$(tput setaf 2); c_fail=$(tput setaf 1)
c_die=$(tput setaf 3); c_dim=$(tput setaf 8); c_off=$(tput sgr0); c_bold=$(tput bold)
else
c_run=; c_ok=; c_fail=; c_die=; c_dim=; c_off=; c_bold=
fi
# a task is "running" if its tmux session still exists
active=""
if command -v tmux >/dev/null 2>&1; then
active=$(tmux ls -F '#{session_name}' 2>/dev/null || true)
fi
is_active() { printf '%s\n' "$active" | grep -qxF -- "$1"; }
# cancel mode — stop a running task by killing its tmux session; the log is kept
if [ "$mode" = cancel ]; then
logfile="$logdir/${target}.log"
if [ ! -f "$logfile" ]; then
echo "no task named '$target'" >&2
exit 1
fi
if is_active "$target"; then
tmux kill-session -t "$target" 2>/dev/null || true
printf '\n>>> cancelled: %s\n' "$(date)" >> "$logfile"
echo "cancelled '$target' (log kept: $logfile)"
exit 0
fi
echo "task '$target' is not running (already finished)" >&2
exit 1
fi
shopt -s nullglob
logs=("$logdir"/*.log)
if [ ${#logs[@]} -eq 0 ]; then
echo "no park tasks found in $logdir"
exit 0
fi
# clear mode — drop logs for finished tasks; running tasks and 'died' crashes are spared
if [ "$mode" = clear ]; then
cleared=0
names=""
for log in "${logs[@]}"; do
name=$(basename "$log" .log)
if is_active "$name"; then continue; fi # never clear a running task
code=$(grep '^>>> exit code:' "$log" | tail -1 | sed 's/^>>> exit code: //' || true)
if [ -z "$code" ]; then continue; fi # 'died' — leave crashes visible
if [ "$code" = "0" ]; then st=ok; else st=failed; fi
if [ "$clear_what" = all ] || [ "$clear_what" = "$st" ]; then
rm -f -- "$log"
cleared=$((cleared + 1))
names="$names $name"
fi
done
if [ "$cleared" -eq 0 ]; then
echo "no ${clear_what/all/finished} tasks to clear"
else
echo "cleared $cleared task(s):$names"
fi
exit 0
fi
printf "%s%-20s %-9s %s%s\n" "$c_bold" "SESSION" "STATUS" "DETAIL" "$c_off"
for log in "${logs[@]}"; do
name=$(basename "$log" .log)
if is_active "$name"; then # session alive → still running
started=$(grep -m1 '^>>> started:' "$log" | sed 's/^>>> started: //' || true)
printf "%-20s %s%-9s%s %s%s%s\n" \
"$name" "$c_run" "running" "$c_off" "$c_dim" "since ${started:-?}" "$c_off"
else
code=$(grep '^>>> exit code:' "$log" | tail -1 | sed 's/^>>> exit code: //' || true)
fin=$(grep '^>>> finished:' "$log" | tail -1 | sed 's/^>>> finished: //' || true)
if [ -n "$code" ] && [ "$code" = "0" ]; then
printf "%-20s %s%-9s%s %s%s%s\n" \
"$name" "$c_ok" "ok" "$c_off" "$c_dim" "exit 0${fin:+, $fin}" "$c_off"
elif [ -n "$code" ]; then
printf "%-20s %s%-9s%s %s%s%s\n" \
"$name" "$c_fail" "FAILED" "$c_off" "$c_dim" "exit $code${fin:+, $fin}" "$c_off"
elif grep -q '^>>> cancelled:' "$log"; then # stopped with parked -k
can=$(grep '^>>> cancelled:' "$log" | tail -1 | sed 's/^>>> cancelled: //' || true)
printf "%-20s %s%-9s%s %s%s%s\n" \
"$name" "$c_die" "cancelled" "$c_off" "$c_dim" "${can:+cancelled $can}" "$c_off"
else # no exit code → killed / OOM / reboot
printf "%-20s %s%-9s%s %s%s%s\n" \
"$name" "$c_die" "died" "$c_off" "$c_dim" "no exit code — killed / reboot?" "$c_off"
fi
fi
if [ "$show_tail" -eq 1 ]; then
last=$(tail -1 "$log" 2>/dev/null || true)
printf " %s└ %s%s\n" "$c_dim" "$last" "$c_off"
fi
done
Drop both in ~/.local/bin, chmod +x, and go:
park dl-llama huggingface-cli download meta-llama/Llama-3.1-8B-Instruct --local-dir ./models/llama
parked # running / ok / FAILED / cancelled / died
parked -l # same, plus each task's last log line
parked -k dl-llama # cancel a running task (kills its tmux session)
parked -c # clear finished (ok + failed) logs; -c ok / -c failed to narrow
cat ~/.park/dl-llama.log
Quote any argument with a space; for pipes, &&, or redirection, put it in a script and run park job ./job.sh.