Command to measure CPU time and RAM usage of process while making sure it does not go over certain ressource limitation I found some old code of mine and am trying to see if it can be improved. The goal is to write a command that measures a process's CPU time and RAM peak usage and kills the process if it exceeds a specified amount of CPU time and RAM usage.

This command is meant to be used on both MAC OSX and Linux.

# Get arguments
MaxMemory="$1"
MaxTime="$2"
Command="$3"
for (( i=4 ; i&lt;="$#"; i++)); do
    Command="${Command} ${!i}"
done

echo -e "MaxMemory = ${MaxMemory}\nMaxTime = ${MaxTime}\nCommand = ${Command}"


#### run the command in the background
${Command} &amp;


#### Get pid
pid=$!
echo "pid = ${pid}"


#### Monitor resources
MemoryPeak=0
timeBefore=$(date +"%s")
while true;do
    # Get memory
    mem=$(ps -o rss= -p $pid)

    # Break if the process has stopped running
    if [[ ${mem} == "" ]]; then
        break
    fi

    # Set the MemoryPeak of memory
    if [ "${mem}" -gt "${MemoryPeak}" ]; then
        MemoryPeak=$mem
    fi

    # If it consumed too much memory, then kill
    if [ "${MemoryPeak}" -gt "${MaxMemory}" ];then
        #echo "process consumed too much memory"
        kill ${pid}
        break
    fi

    # If it consumed too much CPU time, then kill
    timeAfter=$(date +"%s")
    timeUsage=$((timeAfter - timeBefore))
    if [ "${timeUsage}" -gt "${MaxTime}" ];then
        #echo "process consumed too much time"
        kill ${pid}
        break
    fi

    # sleep
    sleep 0.1
done

timeAfter=$(date +"%s")
timeUsage=$((timeAfter - timeBefore))

echo "MEM ${MemoryPeak} TIME ${timeUsage}"


How can this be improved?
Command=&quot;$3&quot;
for (( i=4 ; i&lt;=&quot;$#&quot;; i++)); do
    Command=&quot;${Command} ${!i}&quot;
done
# …
${Command} &amp;


Much cleaner to just reference the end of the $@ array directly.  Since you're running the commands later, and would like to keep quoting intact, use an array:
declare -a Command=( &quot;${@:3}&quot; )

&quot;${Command[@]}&quot; &amp;



# Break if the process has stopped running
if [[ ${mem} == &quot;&quot; ]]; then
    break
fi


The idiomatic way to test for process existence is with kill -0, as in:
 kill -0 $pid || break

Since $mem is a number, or empty, an arithmetic test is good too:
(( mem )) || break



if [ &quot;${mem}&quot; -gt &quot;${MemoryPeak}&quot; ]; then
    MemoryPeak=$mem
fi

if [ &quot;${MemoryPeak}&quot; -gt &quot;${MaxMemory}&quot; ];then
    #echo &quot;process consumed too much memory&quot;
    kill ${pid}
    break
fi


Prefer parentheses over single brackets (or double brackets) for arithmetic operations:
 (( MemoryPeak = ( mem &gt; MemoryPeak ? mem : MemoryPeak ) ))
 
 if (( MemoryPeak &gt; MaxMemory )); then # etc.
 



 # If it consumed too much CPU time, then kill


You're measuring wallclock time, not CPU time.  There's also some slop:  timeBefore is recorded when the script collects it, which might not be close to the binary invocation time.  Better to read the times and cputimes values from ps, and calculate with those:
 MaxElapsed= # …
 MaxCPU= # …
 # …
 read mem dt ct &lt;&lt;&lt; $( ps --no-headers -o rss,etimes,cputimes -p$pid )
 # …
 if (( dt &gt; MaxElapsed || ct &gt; MaxCPU )); then # …
 



timeAfter=$(date +&quot;%s&quot;)
timeUsage=$((timeAfter - timeBefore))


See timing comments above.  If you make the outer loop condition kill -0 $pid, you can simply reference $dt here.

Putting it all together:
 MaxMemory=$1 MaxElapsed=$2 MaxCPU=$3 Command=( &quot;${@:4}&quot; ) MemoryPeak=0
 &quot;${Command[@]}&quot; &amp; pid=$!
 while kill -0 $pid; do
    read mem dt ct &lt;&lt;&lt; $( ps --no-headers -o rss,etimes,cputimes -p$pid )
    (( mem )) || break
    (( MemoryPeak = ( mem &gt; MemoryPeak ? mem : MemoryPeak ) ))
    (( MemoryPeak &gt; MaxMemory || dt &gt; MaxElapsed || ct &gt; MaxCPU )) &amp;&amp; kill $pid &amp;&amp; wait $pid  # loop conditional will break for us
done
echo &quot;MEM $MemoryPeak ELAPSED $dt CPU $ct&quot;