#!/bin/sh set -e kill_child_jobs() { # From https://stackoverflow.com/a/23336595 # Kills all child processes, not just jobs. pkill -P $$ } cleanup() { kill_child_jobs } # From https://unix.stackexchange.com/a/240736 for sig in INT QUIT HUP TERM; do trap " cleanup trap - $sig EXIT kill -s $sig "'"$$"' "$sig" done trap cleanup EXIT verbose=y cpulimit_args="--lazy" limit="" pids="" exes="" paths="" max_processes=100 max_depth=3 watch_interval=0 subprocess_watch_interval=0.5 while [ $# -gt 0 ] do opt="$1" case "$opt" in *=*) arg="${1#*=}" opt="${1%%=*}" argshift=1 ;; *) if [ "$#" -ge 2 ] then arg="$2" else arg="" fi argshift=2 ;; esac case "$opt" in -h|--help) cat </dev/null then ppid="$(echo "$watched" | cut -d: -f1)" original="$(echo "$watched" | cut -d: -f4-)" limit_pids "$(pgrep -P "$ppid")" "$((depth + 1))" "child of $ppid ($original)" fi fi done </dev/null then echo "$watched" fi done)" watched_pids="$tmp" } if [ "$#" -gt 0 ] then "$@" & limit_pid "$!" 0 "program run on command line: $*" fi limit_pids "$pids" 0 while true do clean_dead_cpulimit if [ -z "$exes" ] && [ -z "$paths" ] && [ -z "$watched_pids" ] then # If there's nothing left to wait for, then exit. exit fi limit_by_executable if [ -z "$watched_pids" ] then num_watched_before=0 else num_watched_before="$(echo "$watched_pids" | wc -l)" fi limit_by_subprocess if [ -z "$watched_pids" ] then num_watched_after=0 else num_watched_after="$(echo "$watched_pids" | wc -l)" fi if [ "$num_watched_before" -eq "$num_watched_after" ] then if [ "$watch_interval" = "0" ] then if [ "$num_watched_after" -eq 0 ] then if [ -n "$verbose" ] then echo "No processes found, exiting. Specify --watch-interval to continue scanning for processes." fi exit else if [ -n "$verbose" ] then echo "Identified all processes to limit, waiting." fi wait fi else sleep "$watch_interval" fi else sleep "$subprocess_watch_interval" fi done