Bash脚本启动进程,等待随机,终止进程,重启

时间:2022-10-05 15:45:06

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/ )

我是一个绝对的初学者,我正在尝试创建一个bash脚本来随机化命令行应用程序的启动和退出。我计划在autostart.sh中稍后延迟使用以下内容自动启动脚本(Crunchbang)(见http://interwebworld.co.uk/2011/10/23/how-to-launch-programs-自动启动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:

这基本上是我需要在randomizer.sh脚本中完成的,有点伪代码:

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

到目前为止我的randomizer.sh,我欢迎一些帮助,如下(包含伪代码的残余),以及这里发现的睡眠延迟:http://blog.buberel.org/2010/ 07 / HOWTO随机睡眠持续时间的功能于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.

我“认为”非伪部分应该像它们一样工作,但如果我错了,请纠正我或调整。初始applicationfile命令行按原样工作,我已经测试了进程kill行,它按预期工作。 Applicationfile没有内置的方法从命令行结束自己,但远程机器上的死连接将在本地被杀死5分钟后被杀死,因此在本地杀死它是可以满足我的需要。

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.

我不知道如何处理的是kill上面的行,它首先检查进程是否正在运行。对不起文字的墙,但我想表明我已经做了尽可能多的事情。

2 个解决方案

#1


12  

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

在bash,$!是最后一个启动过程的PID,所以沿着这条线的图案应该有效:

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.

当然,你可以改变你发送的信号,$ RANDOM和你想睡觉的时间等之间的关系。

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.

除非a)睡眠时间很长或b)你的机器启动了很多短暂的过程,否则新进程获得相同PID的可能性不大。在Linux上,PID周期性地分配,最大值为32,765,因此,粗略地说,您必须在睡眠时间内启动许多进程才有可能触及属于不同进程的相同PID。如果这是一个风险,你可以添加一个测试(从技术上讲,这里有一场比赛,但这不太可能是一个问题)。以下似乎会做你想要的。

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.

在kill行上,由于竞争,stderr重定向到/ dev / null是可取的。该过程可能会在ps完成的时间和执行kill的时间之间自然退出,从而产生无害的错误消息。除非PID存在的测试和信号的发送是一致的,否则这种竞赛是不可避免的(并且无害)。

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:

如果打算一次最多运行一个applicationfile实例,那么可以通过替换以下内容来完全避免这种竞争:

# 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:

如果不能使用,Keith Reynolds的测试变体更好,因为它避免了不必要的grep,即使用:

# 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

#2


2  

Try this code for your randomizer.sh

尝试使用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. 而不是使用exec语句。你可以通过留在while循环中来完成你想要做的事情。我提供的randomiser.sh只能从硬盘驱动器中读取一次。

  3. The code { [ condition ] && { command ;} && command runs faster than if [ condition ]; then command, else command; fi
  4. 代码{[condition] && {command;} &&命令比[condition]运行得更快;然后命令,否则命令;科幻

  5. 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.
  6. 将变量$ last_pid赋值给$!,命令ps -p $ last_pid -o comm =将使用$ last_pid的PID吐出进程的名称。如果没有具有该值的PID,则其存在的代码为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

#1


12  

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

在bash,$!是最后一个启动过程的PID,所以沿着这条线的图案应该有效:

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.

当然,你可以改变你发送的信号,$ RANDOM和你想睡觉的时间等之间的关系。

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.

除非a)睡眠时间很长或b)你的机器启动了很多短暂的过程,否则新进程获得相同PID的可能性不大。在Linux上,PID周期性地分配,最大值为32,765,因此,粗略地说,您必须在睡眠时间内启动许多进程才有可能触及属于不同进程的相同PID。如果这是一个风险,你可以添加一个测试(从技术上讲,这里有一场比赛,但这不太可能是一个问题)。以下似乎会做你想要的。

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.

在kill行上,由于竞争,stderr重定向到/ dev / null是可取的。该过程可能会在ps完成的时间和执行kill的时间之间自然退出,从而产生无害的错误消息。除非PID存在的测试和信号的发送是一致的,否则这种竞赛是不可避免的(并且无害)。

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:

如果打算一次最多运行一个applicationfile实例,那么可以通过替换以下内容来完全避免这种竞争:

# 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:

如果不能使用,Keith Reynolds的测试变体更好,因为它避免了不必要的grep,即使用:

# 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

#2


2  

Try this code for your randomizer.sh

尝试使用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. 而不是使用exec语句。你可以通过留在while循环中来完成你想要做的事情。我提供的randomiser.sh只能从硬盘驱动器中读取一次。

  3. The code { [ condition ] && { command ;} && command runs faster than if [ condition ]; then command, else command; fi
  4. 代码{[condition] && {command;} &&命令比[condition]运行得更快;然后命令,否则命令;科幻

  5. 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.
  6. 将变量$ last_pid赋值给$!,命令ps -p $ last_pid -o comm =将使用$ last_pid的PID吐出进程的名称。如果没有具有该值的PID,则其存在的代码为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