Bash script to start process, wait random, kill pr

2019-03-26 19:12发布

I'm an absolute beginner and am trying to create a bash script to randomize the start and exit of a command line app. I plan to autostart the script on boot (Crunchbang) after a slight delay with the following in autostart.sh (found here: http://interwebworld.co.uk/2011/10/23/how-to-launch-programs-automatically-at-startup-in-crunchbang-linux/ )

(sleep 300s && /home/myuser/Scripts/randomizer.sh) &

This is essentially what I need to accomplish in the randomizer.sh script, in a bit of pseudocode:

start applicationfile
wait a random period of time
if applicationfile is still running
    kill its process
    wait a random period of time
    exit this script and restart this script
else exit this script and restart this script

The randomizer.sh as I have it so far and which I'd welcome some help with, is as follows (containing remnants of the pseudocode), and the sleep delay found here: http://blog.buberel.org/2010/07/howto-random-sleep-duration-in-bash.html

/path/to/applicationfile -s 111.222.333.444 -u username -p password
sleep $[ ( $RANDOM % 150 ) + 60 ]m
if applicationfile is still running
    kill $(ps aux | grep '[u]sername' | awk '{print $2}')
    sleep $[ ( $RANDOM % 150 ) + 60 ]m
    exec $randomizer.sh
else exec $randomizer.sh

I "think" the non-pseudo parts should work pretty much as they are, but please correct me or adjust if I'm wrong. The initial applicationfile command line works as it is, and I already tested the process kill line and it works as expected. Applicationfile doesn't have a built-in way to end itself from commandline, but the dead connection on the remote machine will be killed after 5 minutes of being killed locally, so killing it locally is acceptable for my needs.

What I don't have any idea how to handle is the line above the kill, which checks "if" the process is running in the first place. Sorry for the wall of text but I wanted to show I've done as much as I could already.

2条回答
趁早两清
2楼-- · 2019-03-26 19:20

In bash, $! is the PID of the last launched process, so something patterned along the lines of this should work:

mycommand &
last_pid=$!
sleep( $RANDOM )
kill -KILL $last_pid

You can, of course, fiddle around to change the signal you send, the relationship between $RANDOM and the time you want to sleep, etc.

It's somewhat unlikely that a new process will get the same PID unless either a) the sleep time is very long or b) your machine launches a lot of short-lived processes. On Linux, PIDs are allocated cyclically with a max of 32,765, so, roughly speaking, you would have to have launched about that many processes in the sleep time to risk hitting the same PID belonging to a different process. If that's a risk, you could add a test (technically, there's a race here, but it's very unlikely to be a problem). The following seems like it would do what you want.

signal=KILL

sleep_a_while () {
    sleep $[ ( $RANDOM % 150 ) + 60 ]m
}

while true; do
    # Note: command launched in background:
    /path/to/applicationfile -s 111.222.333.444 -u username -p password &

    # Save PID of command just launched:
    last_pid=$!

    # Sleep for a while:
    sleep_a_while

    # See if the command is still running, and kill it and sleep more if it is:
    if ps -p $last_pid -o comm= | grep -qs '^applicationfile$'; then
        kill -$signal $last_pid 2> /dev/null
        sleep_a_while
    fi

    # Go back to the beginning and launch the command again
done

I've replaced the self-exec with an equivalent loop.

On the kill line, the redirect of stderr to /dev/null is desirable because of a race. The process might exit naturally between the time the ps completes and the time that the kill is executed, resulting in a harmless error message. This race is unavoidable (and harmless) unless the test that the PID exists and the sending of the signal are coincident.

If there is intended to be at most one instance of applicationfile running at a time, then this race can be avoided entirely by replacing:

# See if the command is still running, and kill it and sleep more if it is:
if ps -p $last_pid -o comm= | grep -qs '^applicationfile$'; then
    kill -$signal $last_pid 2> /dev/null
    sleep_a_while
fi

With:

killall -q applicationfile && sleep_a_while

If this cannot be used, Keith Reynolds's variant of the test is better, since it avoids an unnecessary grep, i.e. using:

# See if the command is still running, and kill it and sleep more if it is:
if [ "$(ps -p $last_pid -o comm=)" = "applicationfile" ]; then
    kill -$signal $last_pid 2> /dev/null
    sleep_a_while
fi
查看更多
beautiful°
3楼-- · 2019-03-26 19:29

Try this code for your randomizer.sh

min_val=60
range=150
while true ; do
    run_this_command &
    last_pid=$!
    sleep $[ ( $RANDOM % $range ) + $min_val ]
    { [ "$(ps -p $last_pid -o comm= )" ] && \
      [ "$(ps -p $last_pid -o comm= )" = run_this_command ]; 
    } && { kill -KILL $last_pid ;}
done

Some notes:

  1. Rather than using the exec statement. You can accomplish what your trying to do more simply by staying inside a while loop. The randomiser.sh I present is only read from the hard drive once.
  2. The code { [ condition ] && { command ;} && command runs faster than if [ condition ]; then command, else command; fi
  3. With the variable $last_pid assigned to the value of $!, the command ps -p $last_pid -o comm= will spit out the name of the process with the PID of $last_pid. If there is no PID with that value then its exist code is 1.

Amended to meet the additional random wait period before start requirement:

# Minimum and range values for random Wait before start in seconds
MinA=60;RangeA=150 
# Minimum and range values for random Wait before kill in seconds
MinB=60; RangeB=150 # 
while true ; do
    sleep $[ ( $RANDOM % $RangeA ) + $MinA ] 
    run_this_command &
    last_pid=$!
    sleep $[ ( $RANDOM % $RangeB ) + $MinB ] 
    { [ "$(ps -p $last_pid -o comm= )" ] && \
      [ "$(ps -p $last_pid -o comm= )" = run_this_command ]
    } && \{ kill -KILL $last_pid ;}
done
查看更多
登录 后发表回答