4
\$\begingroup\$

This script's only responsibility is to keep a process alive. Forever. It's sole dependencies shall be bash and coreutils. I'm not sure if it would be compatible with sh, that would be even better.

#!/bin/bash
#
# (c) Netcetera AG
# Author:  Christian Mäder
date="10.07.2015"
version="1.0"
#

respawn() {
    while true; do
        $myprocess
        exit_code=$?

        now=`date +"%Y-%m-%dT%H:%M:%S%z"`

        if [ $exit_code -ne 0 ] || [ $force -eq 1 ]; then
            if [ $quite -eq 0 ]; then
                echo "$now | service $myprocess crashed with code $?. Restarting after ${timeout}s." >&2
            fi
            sleep $timeout
        else
            break
        fi
    done
}

help() {
    echo "Usage: $1 [-f] [-q] [-t #] \"command\""
    echo "       $1 -v"
    echo "       $1 -h"
    echo
    echo "  -v             : Shows the version of this tool."
    echo "  -h             : Shows this help text."
    echo "  -f             : Restart the program even if it exited with an exit"
    echo "                   status of 0."
    echo "  -q             : If set, this script won't write anything to stdout or stderr."
    echo "  -t #           : Seconds for how long the script should wait until"
    echo "                   the restart happend."
    echo "  command        : The command to execute continuously until it exits"
    echo "                   gracefully or forever if -f is set."
    echo
    echo "Example: $1 -f -t 2 \"echo hello, see you again in two.\"" 
}

about() {
    echo "Respawns a process which dies. Hence it keeps it runnig forever."
    echo "Version $version from $date."
}

while true; do
    case "$1" in
        "--help")
            help $0
            break
            ;;
        "-h")
            help $0
            break
            ;;
        "-v")
            about
            break
            ;;
        "-q")
            quite=1
            shift
            ;;
        "-f")
            force=1
            shift
            ;;
        "-t")
            if [[ $2 =~ '^[0-9]+$' ]]; then
                timeout=$2
                shift
            fi
            shift
            ;;
        *)
            if [ $# -eq 0 ]; then
                about
                echo
                help $0
            else
                quite=${quite-0}
                force=${force-0}
                timeout=${timeout-"1"}
                myprocess=$1
                respawn
            fi
            break
            ;;
    esac
done

For everyone's reference have I created a gist with an updated version which incorporated the feedback.

\$\endgroup\$
4
  • 1
    \$\begingroup\$ There is already a process for this: init or upstart or god etc.. \$\endgroup\$ Sep 4 '15 at 6:44
  • 2
    \$\begingroup\$ You can have Shellcheck check for POSIX compatibility by changing #!/bin/bash to #!/bin/sh. \$\endgroup\$
    – mkrieger1
    Sep 4 '15 at 9:31
  • \$\begingroup\$ To comply with POSIX, you'd also have to change now=`date +"%Y-%m-%dT%H:%M:%S%z"` to now=$(date +"%Y-%m-%dT%H:%M:%S%z") \$\endgroup\$ Sep 15 '15 at 16:55
  • \$\begingroup\$ This has already been mentioned in @ErikR's answer. Thanks anyway :) \$\endgroup\$
    – cimnine
    Sep 16 '15 at 14:12
4
\$\begingroup\$

By quite, you mean quiet, I think. You also have a typo with "runnig".

The script would be better self-documented if you put the about and help functions first.

In help, instead of echoing $1, which may be a path, it would be more conventional to echo just the basename.

It would be more elegant for the respawn function to accept the command as a parameter rather than using the $myprocess global variable. "Process", to me, implies a particular instance, i.e. a running program with a PID, so I'd rename myprocess to command. You only need to execute date if you're actually going to print a log message. I'd rearrange the conditions to emphasize how to exit the loop and to reduce nesting.

respawn() {
    command="$1"
    while true; do
        $command
        exit_code=$?

        if [ $exit_code -eq 0 -a $force -ne 1 ]; then
            break
        elif [ $quiet -ne 1 ]; then
            echo "`date +"%Y-%m-%dT%H:%M:%S%z"` | service $command exited with code $?. Restarting after ${timeout}s." >&2
        fi
        sleep $timeout
    done
}

In the argument parser, the --help and the -h conditions are identical. You should write

case "$1" in
    "--help"|"-h")
        help "$0"
        break
        …

Note that double-quoting variables is a good habit.

I don't believe that the [[ $2 =~ '^[0-9]+$' ]] test is available in traditional Bourne shell. If portability is a concern, you could write the not-quite-equivalent test

case "$2" in
    [0-9]*) timeout=$2 ; shift
    ;;
esac
shift
\$\endgroup\$
0
3
\$\begingroup\$

Some things that ShellCheck spotted:

now=`date +"%Y-%m-%dT%H:%M:%S%z"`
    ^--- use $(...) instead of legacy `...`

if [ $exit_code -ne 0 ] || [ $force -eq 1 ]; then
                             ^--- use "$force" to prevent globbing
                                  and word splitting

(Note that no quoting of $exit_code is needed since it was assigned from $?.)

if [ $quite -eq 0 ]; then
     ^--- use double quotes  (should this be $quiet ?)

sleep $timeout
      ^--- use double quotes

help $0
     ^--- use "$0"  (3 different places)

if [[ $2 =~ '^[0-9]+$' ]]; then
            ^--- Don't quote rhs of =~, it'll match
                 literally rather than as a regex.
\$\endgroup\$
1
  • \$\begingroup\$ Of course, I forgot about ShellCheck. Thanks for the reminder! \$\endgroup\$
    – cimnine
    Sep 4 '15 at 8:26

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.