Evitar que o script duplicado seja executado no sametime

5

Estou usando o escaninho para buscar alguns recursos e quero torná-lo um trabalho cron que pode ser iniciado a cada 30 minutos.

O cron:

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

Como o script mostrado tentei matar o processo de script e o processo filho (escasso) também.

No entanto, quando tentei executar dois scripts, a instância mais recente do script não elimina a instância anterior.

Como consertar isso?

Atualização:

Tenho mais de um script .sh scrapy que é executado em diferentes frequências configuradas em cron .

Atualização 2 - Teste da resposta de Serg :

Todos os cron jobs foram parados antes de eu executar o teste.

Em seguida, abro três janelas do terminal dizendo que elas são chamadas de w1 w2 e w3 e executam os comandos nas seguintes ordens:

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)

Neste momento, tenho que usar pkill scrapy para interromper o ID com 1234

    
por hguser 03.08.2016 / 13:47

6 respostas

8

Melhor abordagem seria usar um script de wrapper, que irá chamar o script principal. Isso ficaria assim:

#!/bin/bash
# This is /home/user/bin/wrapper.sh file
pkill -f 'main_script.sh'
exec bash ./main_script.sh

É claro que o wrapper deve ser nomeado de forma diferente. Dessa forma, pkill pode pesquisar apenas por seu script principal. Desta forma, o seu script principal reduz a isso:

#!/bin/sh
cd /home/user/spiders/goods
PATH=$PATH:/usr/local/bin
export PATH
scrapy crawl good

Observe que, no meu exemplo, estou usando ./ porque o script estava no meu diretório de trabalho atual. Use o caminho completo para o seu script para obter melhores resultados

Eu testei essa abordagem com um script principal simples que apenas executa o script infinito while loop e wrapper. Como você pode ver na captura de tela, o lançamento da segunda instância do wrapper elimina a anterior

Seu script

Este é apenas um exemplo. Lembre-se de que não tenho acesso ao escasso para realmente testar isso, então ajuste isso conforme necessário para sua situação.

Sua entrada no cron deve ter esta aparência:

0,30 * * * * /home/us/jobs/scrapy_wrapper.sh

Conteúdo de scrapy_wrapper.sh

#!/bin/bash
pkill -f 'run_scrapy.sh'
exec sh /home/us/jobs/run_scrapy.sh

Conteúdo de 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
    
por Sergiy Kolodyazhnyy 07.08.2016 / 05:33
2

Talvez você deva monitorar se o script está sendo executado, criando um arquivo pid do script de shell pai e tentando matar o script de shell pai em execução anterior, verificando o arquivo pid. Algo parecido com isso

#!/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"
    
por iuuuuan 09.08.2016 / 12:25
2

Se eu entendi o que você está fazendo corretamente, você quer chamar um processo a cada 30 minutos (via cron). No entanto, quando você inicia um novo processo via cron, você quer matar qualquer versão existente ainda em execução?

Você pode usar o comando "timeout" para garantir que, se for interrompido, seja forçado a encerrar se ainda estiver em execução após 30 minutos.

Isso tornaria seu script assim:

#!/bin/sh
cd ~/spiders/goods
PATH=$PATH:/usr/local/bin
export PATH
timeout 30m scrapy crawl good

observe o tempo limite adicionado na última linha

Defina a duração para "30m" (30 minutos). Você pode querer escolher um tempo um pouco mais curto (digamos, 29m) para garantir que o processo tenha terminado antes do início do próximo trabalho.

Note que se você alterar o intervalo de spawn no crontab, você terá que editar o script também

    
por Nick Sillito 12.08.2016 / 14:32
1

Como pkill termina apenas o processo especificado, devemos terminar seus subprocessos filhos usando a opção -P . Então, o script modificado ficará assim:

#!/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 executa o comando definido (entre aspas duplas) no evento EXIT , ou seja, quando run_scrapy.sh é finalizado. Existem outros eventos, você os encontrará em help trap .
pgrep -o encontra a instância mais antiga do processo com o nome definido.

PS Sua idéia com grep -v $$ é boa, mas não retornará o PID de outra instância de run_scrapy.sh , porque $$ será o PID do subprocesso $(pgrep run_scrapy.sh | grep -v $$) , não o PID de run_scrapy.sh que o iniciou. É por isso que usei outra abordagem.
PPS Você encontrará alguns outros métodos de encerrar subprocessos no Bash aqui .

    
por whtyger 09.08.2016 / 11:41
0

Bem, eu tive um problema parecido com o C usando o popen () e gosto de matar depois de um timeout timeout e de todos os childs. O truque é definir um ID de grupo de processo ao iniciar seu pai para não me matar. como fazer isso pode ser lido aqui: link com "ps -eo pid, ppid, cmd, etime" você pode filtrar ao longo do tempo de execução. Assim, com ambas as informações, você deve conseguir filtrar todos os processos antigos e eliminá-los.

    
por 0x0C4 09.08.2016 / 16:11
0

Você pode verificar uma variável de ambiente para rastrear o status do script e configurá-lo apropriadamente no início do script, algo como este código do psuedo:

if "$SSS" = "Idle"
then 
    set $SSS=Running"
    your script
    set $SSS="Idle"

Você também pode acompanhar o status criando / verificando / excluindo um arquivo de marcador como touch /pathname/myscript.is.running e usando se existir no lançamento e rm /pathname/myscript.is.running no final.

Essa abordagem permitirá que você use diferentes identificadores para seus diferentes scripts de escória, para evitar eliminar os errados.

Independentemente de como você controla o status do seu script e se você lida com o problema prevenindo o lançamento ou matando o processo em execução, acredito que usar um script de wrapper como sugerido por @JacobVlijm & amp; @Serg tornará sua vida muito mais fácil.

    
por Elder Geek 10.08.2016 / 21:46