![防止重复脚本同时运行](https://linux22.com/image/1139163/%E9%98%B2%E6%AD%A2%E9%87%8D%E5%A4%8D%E8%84%9A%E6%9C%AC%E5%90%8C%E6%97%B6%E8%BF%90%E8%A1%8C.png)
我正在使用 scrapy 来获取一些资源,并且我想让它成为cron
每 30 分钟启动一次的工作。
计划任务:
0,30 * * * * /home/us/jobs/run_scrapy.sh`
run_scrapy.sh
:
#!/bin/sh
cd ~/spiders/goods
PATH=$PATH:/usr/local/bin
export PATH
pkill -f $(pgrep run_scrapy.sh | grep -v $$)
sleep 2s
scrapy crawl good
正如脚本所示,我还尝试终止脚本进程和子进程(scrapy)。
但是,当我尝试运行该脚本的两个实例时,较新的实例并不会终止较旧的实例。
如何修复?
更新:
我有多个.sh
以不同频率运行的 scrapy 脚本cron
。
更新2-测试Serg
的答案:
在我运行测试之前,所有的 cron 作业都已停止。
然后我打开三个终端窗口,分别命名为 w1 w2 和 w3,并按以下顺序运行命令:
Run `pgrep scrapy` in w3, which print none.(means no scrapy running at the moment).
Run `./scrapy_wrapper.sh` in w1
Run `pgrep scrapy` in w3 which print one process id say it is `1234`(means scrapy have been started by the script)
Run `./scrapy_wrapper.sh` in w2 #check the w1 and found the script have been terminated.
Run `pgrep scrapy` in w3 which print two process id `1234` and `5678`
Press <kbd>Ctrl</kbd>+<kbd>C</kbd> in w2 (twice)
Run `pgrep scrapy` in w3 which print one process id `1234` (means scrapy of `5678` have been stopped)
此刻,我必须使用pkill scrapy
id 为 的 scrapy 来停止1234
答案1
更好的方法是使用包装脚本,它将调用主脚本。它看起来如下:
#!/bin/bash
# This is /home/user/bin/wrapper.sh file
pkill -f 'main_script.sh'
exec bash ./main_script.sh
当然,包装器必须以不同的方式命名。这样,pkill
就可以只搜索您的主脚本。这样您的主脚本就简化为:
#!/bin/sh
cd /home/user/spiders/goods
PATH=$PATH:/usr/local/bin
export PATH
scrapy crawl good
请注意,在我的示例中,我使用的是./
因为脚本位于我当前的工作目录中。使用脚本的完整路径可获得最佳效果
我已经用一个简单的主脚本测试了这种方法,该脚本只运行无限的 while 循环和包装器脚本。如您在屏幕截图中看到的那样,启动包装器的第二个实例会杀死前一个
你的脚本
这只是一个例子。请记住,我无法访问 scrapy 来实际测试这一点,因此请根据您的情况进行调整。
你的 cron 条目看起来应该像这样:
0,30 * * * * /home/us/jobs/scrapy_wrapper.sh
内容scrapy_wrapper.sh
#!/bin/bash
pkill -f 'run_scrapy.sh'
exec sh /home/us/jobs/run_scrapy.sh
内容run_scrapy.sh
#!/bin/bash
cd /home/user/spiders/goods
PATH=$PATH:/usr/local/bin
export PATH
# sleep delay now is not necessary
# but uncomment if you think it is
# sleep 2
scrapy crawl good
答案2
如果我理解正确的话,您希望每 30 分钟调用一次进程(通过 cron)。但是,当您通过 cron 启动新进程时,您是否想终止仍在运行的任何现有版本?
您可以使用“timeout”命令来确保如果 scrappy 在 30 分钟后仍在运行则强制终止。
这将使你的脚本看起来像这样:
#!/bin/sh
cd ~/spiders/goods
PATH=$PATH:/usr/local/bin
export PATH
timeout 30m scrapy crawl good
注意最后一行添加的超时
我已将持续时间设置为“30m”(30 分钟)。您可能希望选择稍短的时间(例如 29m),以确保在下一个作业开始之前该过程已终止。
请注意,如果你更改了 crontab 中的生成间隔,则还必须编辑脚本
答案3
太棒了。稍作更新,脚本就可以自行确定自己的文件名,而无需硬编码:
#!/bin/bash
# runchecker.sh
#this script obtains the name of the script and then
#checks if the script is already running or not
#if scripts already runs it exits
filename=$(basename $0)
echo running now $filename
pids=($(pidof -x $filename))
if [ ${#pids[@]} -gt 1 ] ; then
echo "Script already running by pid ${pids[1]}"
exit
fi
echo "Starting service "
sleep 1000
enter code here
答案4
由于pkill
仅终止指定的进程,我们应该使用-P
选项终止其子进程。因此修改后的脚本将如下所示:
#!/bin/sh
cd /home/USERNAME/spiders/goods
PATH=$PATH:/usr/local/bin
export PATH
PID=$(pgrep -o run_scrapy.sh)
if [ $$ -ne $PID ] ; then pkill -P $PID ; sleep 2s ; fi
scrapy crawl good
trap
EXIT
在事件(即run_scrapy.sh
终止时)上运行定义的命令(在双引号中) 。还有其他事件,您可以在 中找到它们help trap
。
pgrep -o
查找具有定义名称的进程的最旧实例。
附言您的想法grep -v $$
很好,但它不会返回其他实例的 PID run_scrapy.sh
,因为$$
这将是子进程的 PID $(pgrep run_scrapy.sh | grep -v $$)
,而不是启动它的 PID run_scrapy.sh
。这就是我使用另一种方法的原因。
聚苯硫醚你会发现在 Bash 中终止子进程的其他一些方法这里。