2
\$\begingroup\$

Tonight I tried to write a POSIX shell script which would allow me to check how long a process is running (via it's ID) and be able to handle multiple inputs:

#!/bin/sh 

is_this_a_number()
{
    if [ "$1" -eq "$1" ] 2> /dev/null
    then
        return 0
    else
        return 1
    fi
}

does_pid_exist()
{
    if ps --pid "$1" > /dev/null 2>&1
    then
        return 0
    else
        return 1
    fi
}

how_long_pid_has_been_running()
{
    ps --pid "$1" --format etime=
}

trim_leading_and_trailing_spaces()
{
    awk '{$1=$1;print}'
}


arg_pos=0

while [ "$#" -gt 0 ]
do

    arg_val="$1"
    arg_pos=$((arg_pos + 1))

    if ! is_this_a_number "$arg_val"
    then

        echo "The argument at position $arg_pos is not a number."

    else

        echo "Process ID $arg_val"

        if ! does_pid_exist "$arg_val"
        then

            echo "This process does not exist."

        else

            how_long_pid_has_been_running "$arg_val" | trim_leading_and_trailing_spaces

        fi

    fi

    printf "\\n"

    shift

done

Usage:

./how-long-pid-is-running 1 10078 c 40545454

Where 10078 is an instance of Chrome.

The output is as follows:

Process ID 1
09:04:31

Process ID 10078
07:32:32

The argument at position 3 is not a number.

Process ID 40545454
This process does not exist.
\$\endgroup\$
2
\$\begingroup\$
  • GNU long options are not POSIX, so instead of ps --pid "$1" --format etime=, write ps -p "$1" -o etime=.
  • kill -s 0 "$1" >/dev/null 2>&1 is much faster than ps --pid "$1" > /dev/null 2>&1, partly because kill is a builtin in most shells. Signal 0 is used to test for the existence of a process without sending it a signal.
  • Consider redirecting the echo messages to the appropriate output streams, i.e. stdout and stderr.
  • is_this_a_number, does_pid_exist: these are used directly in if clauses, so it'd read more naturally if they are renamed to is_a_number and pid_exists.
  • A minor point here, but I'd reverse the if-else statements to remove the ! negations.
  • Another minor point: during the short time span between testing the existence of a PID and querying the elapsed time since the process with that PID was started, the process with that PID could have finished running or even been substituted with a newly spawned one. You could avoid that by running the second command first, saving the output in a variable and then check for nullness, like thus:

    echo "Process ID $arg_val"
    running_time=$(how_long_pid_has_been_running "$arg_val" 2>/dev/null | 
        trim_leading_and_trailing_spaces)
    if [ -z "${running_time}" ]
    then
        echo "This process does not exist."
    else
        echo "${running_time}"            
    fi
    
  • I've seen you write printf "\\n" in another post also, so I'm curious: is it any different from printf "\n"? And in any case, an echo would do the job just as well.
\$\endgroup\$
1
\$\begingroup\$

Functions forward the exit status of the last command, so the pattern

f()
{
    if $command
    then
        return 0
    else
        return 1
    fi
}

can be replaced with

f()
{
    $command
}

unless you really must force all error returns to 1.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.