スクレイピーを使用していくつかのリソースをフェッチしていますが、30分ごとに開始できるcron
ジョブとして作成したいです。
Cron:
0,30 * * * * /home/us/jobs/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
スクリプトが示したように、スクリプトプロセスと子プロセス(スクレイピー)も強制終了しようとしました。
ただし、2つのスクリプトを実行しようとしたときに、スクリプトの新しいインスタンスが古いインスタンスを強制終了することはありません。
それを修正する方法は?
更新:
cron
で設定された異なる周波数で実行される複数の.sh
スクレイピースクリプトがあります。
更新2-Serg
の回答のテスト:
テストを実行する前に、すべてのcronジョブが停止しました。
次に、w1、w2、w3という名前の3つのターミナルウィンドウを開き、次の順序でコマンドを実行します。
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 `Ctrl+C` in w2(twice)
Run `pgrep scrapy` in w3 which print one process id `1234` (means scrapy of `5678` have been stopped)
現時点では、pkill scrapy
のIDでスクレイピーを停止するには1234
を使用する必要があります
より良いアプローチは、メインスクリプトを呼び出すラッパースクリプトを使用することです。これは次のようになります。
#!/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ループとラッパースクリプトを実行するだけの単純なメインスクリプトでテストしました。スクリーンショットでわかるように、ラッパーの2番目のインスタンスを起動すると、以前の
スクリプト
これは単なる例です。私は実際にこれをテストするためにスクレイピーにアクセスできないので、状況に応じてこれを調整してください。
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
親シェルスクリプトpidファイルを作成してスクリプトが実行されているかどうかを監視し、pidファイルをチェックして以前実行中の親シェルスクリプトを強制終了することをお勧めします。そんな感じ
#!/bin/sh
PATH=$PATH:/usr/local/bin
PIDFILE=/var/run/scrappy.pid
TIMEOUT="10s"
#Check if script pid file exists and kill process
if [ -f "$PIDFILE" ]
then
PID=$(cat $PIDFILE)
#Check if process id is valid
ps -p $PID >/dev/null 2>&1
if [ "$?" -eq "0" ]
then
#If it is valid kill process id
kill "$PID"
#Wait for timeout
sleep "$TIMEOUT"
#Check if process is still running after timeout
ps -p $PID >/dev/null 2>&1
if [ "$?" -eq "0" ]
then
echo "ERROR: Process is still running"
exit 1
fi
fi
fi
#Create PID file
echo $$ > $PIDFILE
if [ "$?" -ne "0" ]
then
echo "ERROR: Could not create PID file"
exit 1
fi
export PATH
cd ~/spiders/goods
scrapy crawl good
#Delete PID file
rm "$PIDFILE"
私があなたが正しくしていることを理解しているなら、あなたは30分ごとに(cron経由で)プロセスを呼び出したいです。ただし、cronを介して新しいプロセスを開始するとき、まだ実行中の既存のバージョンを強制終了したいですか?
「timeout」コマンドを使用して、30分後に実行中の場合、scrappyが強制的に終了するようにすることができます。
これにより、スクリプトは次のようになります。
#!/bin/sh
cd ~/spiders/goods
PATH=$PATH:/usr/local/bin
export PATH
timeout 30m scrapy crawl good
最後の行に追加されたタイムアウトに注意してください
期間を「30m」(30分)に設定しました。次のジョブが開始される前にプロセスが確実に終了するように、少し短い時間(29mなど)を選択することもできます。
Crontabで生成間隔を変更する場合は、スクリプトも編集する必要があることに注意してください
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
は、定義された名前を持つプロセスの最も古いインスタンスを見つけます。
PSgrep -v $$
を使用したアイデアは優れていますが、run_scrapy.sh
の他のインスタンスのPIDは返されません。 $$
は、サブプロセスのPID $(pgrep run_scrapy.sh | grep -v $$)
であり、それを開始したrun_scrapy.sh
のPIDではありません。それが私が別のアプローチを使用した理由です。
P.P.S。Bash here には、サブプロセスを終了する他の方法がいくつかあります。
まあ、私はpopen()を使用してCで同様の問題があり、タイムアウトの親とすべての子の後に殺すのが好きです。トリックは、自分自身を殺さないように親を開始するときにプロセスグループIDを設定することです。これを行う方法はここで読むことができます: https://stackoverflow.com/questions/6549663/how-to-set-process-group-of-a-Shell-script with "ps -eo pid、ppid、cmd、etime "ランタイムに沿ってフィルタリングできます。したがって、両方の情報を使用すると、すべての古いプロセスをフィルタリングして、それらを強制終了できるはずです。
環境変数をチェックしてスクリプトのステータスを追跡し、スクリプト開始時に適切に設定して、次のような擬似コードを作成できます。
if "$SSS" = "Idle"
then
set $SSS=Running"
your script
set $SSS="Idle"
touch /pathname/myscript.is.running
などのマーカーファイルを作成/確認/削除し、起動時に存在する場合はrm /pathname/myscript.is.running
を使用して、ステータスを追跡することもできます。
このアプローチにより、スクレイピースクリプトごとに異なる識別子を使用して、間違ったスクリプトを殺すことを防ぐことができます。
スクリプトの状態をどのように追跡するか、起動の防止または実行中のプロセスの強制終了によって問題に対処するかどうかに関係なく、@ JacobVlijmおよび@Sergが示唆するラッパースクリプトを使用することで、作業がはるかに楽になると思います。