Eu tenho essa estrutura de diretórios em /var/sync
:
# tree -d /var/sync/
/var/sync/
├── sync_bi
│ ├── replicator => Replicator.php
│ sync.php
├── sync_pfizer
│ ├── replicator => Replicator.php
│ sync.php
│ replicator.sh
│ sync.sh
Como você pode ver em cada sync_*
diretório existe um script chamado sync.php
e em cada replicator
diretório existe um script chamado Replicator.php
. Preciso executar esses scripts todas as noites. Replicator.php
deve ser executado às 00h00 e sync.php
às 12h30. Eu sei que o caminho certo aqui é obviamente um cron job. Fiz dois scripts replicator.sh
e sync.sh
compartilhei quase o mesmo código, a principal diferença é o nome do script. Veja o código abaixo:
replicator.sh
#! /bin/bash
cd $1
/usr/bin/php Replicator.php
sync.sh
#! /bin/bash
cd $1
/usr/bin/php sync.php
Então adicionei cada um ao cronjob da seguinte maneira:
0 0 * * * /var/sync/replicator.sh /var/sync/sync_bi/replicator/
0 30 * * * /var/sync/sync.sh /var/sync/sync_bi/
0 2 * * * /var/sync/replicator.sh /var/sync/sync_pfizer/replicator/
30 2 * * * /var/sync/sync.sh /var/sync/sync_pfizer/
Minha ideia por trás de toda essa explicação é otimizar meu ambiente alterando o script para apenas um que me permita realizar todas as ações em apenas uma chamada cronjob e manter menos arquivos para manter. Então fiz uma pequena pesquisa e encontrei alguns tópicos úteis:
- Usar
find some/dir -type f -execdir somecommand {} \;
é uma maneira adequada de conseguir a mesma coisa que estou fazendo comcd
E movendo-se para cada diretório no caminho raiz - no meu caso
/var/sync
for D in ./*; do if [ -d "$D" ]; then cd "$D" run_something cd .. fi done
Este é um pseudocódigo do que estou falando (caso não esteja claro o suficiente):
read tree structure at /var/sync
foreach (directory in root+path)
if (directory_name is replicator)
cd replicator/
/usr/bin/php replicator.php
else if (directory_name is sync_*)
cd sync_*/
/usr/bin/php sync.php
endforeach
Não sou tão especialista em bash, então peço ajuda para traduzir esse pseudocódigo em algo funcional que me permita alcançar o que procuro, algum conselho e/ou ajuda?
ATUALIZAR
Talvez eu esteja errado, mas não estou acompanhando o que vocês estão tentando fazer, então aqui está a ordem dos eventos:
# Run Replicator at mid day and mid night
# this two can run at same time since they are pushing data to different DBs
/var/sync/sync_bi/replicator/Replicator.php
/var/sync/sync_pfizer/replicator/Replicator.php
# Run sync at mid day and mid night
# this two should run one first and the second after 30 min since they are
# pushing data to the same DBs and second depends on first one
/var/sync/sync_bi/sync.php
/var/sync/sync_pfizer/sync.php
Tendo essa informação (e desculpe por não esclarecer isso primeiro) como suas soluções deveriam funcionar? Como você os tem agora ou precisa mudar alguma coisa para cobrir isso?
Responder1
Isso traduz seu pseudocódigo para bash:
#!/bin/bash
cd /var/sync
for dir in sync*/ ; do
( # Subshell to make "cd" not global.
cd "$dir"
[[ -f sync.php ]] && /usr/bin/php sync.php
if [[ -d replicator ]] ; then
cd replicator
[[ -f replicator.php ]] && /usr/bin/php replicator.php
fi
)
done
Ele executa o sync.php e o replicator.php ao mesmo tempo, mas originalmente você queria chamá-los em momentos diferentes. Talvez algo como
#!/bin/bash
run=$1
cd /var/sync
for dir in sync*/ ; do
( # Subshell to make "cd" not global.
cd "$dir"
[[ -f "$run" ]] && /usr/bin/php "${run##*/}"
)
done
chamado do cron como
0 30 * * * /path/to/script.sh sync.php
0 0 * * * /path/to/script.sh replicator/Replicator.php
${run##*/}
remove tudo antes do último /
, ou seja, retorna apenas o nome da base.
Atualizar
Se você deseja executar os scripts da seguinte maneira:
0:00 sync_a/sync.php
0:30 sync_a/replicator/Replicator.php
1:00 sync_b/sync.php
1:30 sync_b/replicator/Replicator.php
2:00 sync_c/sync.php
...
etc., você pode simplesmente regenerar o crontab toda vez que alterar a estrutura de diretórios:
#!/bin/bash
add=$1
php=/usr/bin/php
hour=0
minute=0
update_time () {
(( minute += add ))
if (( minute >= 60 )) ; then
(( hour += minute / 60 ))
(( minute %= 60 ))
fi
if (( hour > 11 )) ; then
echo "Can't fit into a day!"
exit 1
fi
}
cd /var/sync
for dir in sync*/ ; do
if [[ -f "$dir"/sync.php ]] ; then
echo $minute $hour 0 0 0 "$php" /path/to/run.sh "$dir" sync.php
update_time
if [[ -f "$dir"/replicator/Replicator.php ]] ; then
echo $minute $hour 0 0 0 "$php" path/to/run.sh "$dir" Replicator.php
fi
update_time
fi
done
Ligue como generate_crontab.sh 30
ou similar. Observe que alguns dos diretórios podem ser ignorados após uma alteração, pois os arquivos são classificados em ordem alfabética.
run.sh
deveria apenas
dir=$1
script=$2
if [[ -f "$dir$script" ]] ; then
cd "$dir"
/usr/bin/php $script
fi
Responder2
A abordagem mais simples seria dois find
comandos:
find /var/sync/ -name 'sync.php' -execdir php {} \;
find /var/sync/ -name 'Replicator.php' -execdir php {} \;
Isso procurará por arquivos chamados sync.php
ou Replicator.php
, então cd em seu diretório pai e os executará com php
. Você pode adicionar os comandos diretamente ao seu crontab:
0 30 * * * find /var/sync/ -name 'sync.php' -execdir php {} \;
0 0 * * * find /var/sync/ -name 'Replicator.php' -execdir php {} \;
Se precisar que os Replicator
scripts sejam executados com uma pausa de 30 minutos entre eles, você pode fazer algo como:
find /var/sync/ -name 'Replicator.php' -execdir php {} \; -exec sh -c "sleep 30m" \;
Isso primeiro executará o script e, em seguida, aguardará 30 minutos antes de passar para o próximo.
Finalmente, se você precisar ter certeza de que i) você nunca terá >1 sync.php
em execução ao mesmo tempo e ii) cada script será executado após a conclusão do script sync
correspondente , você pode usar isto como o comando fornecido ao cron:replicator
find /var/sync/ -name 'Replicator.php' -execdir php {} \; ; find /var/sync/ -name 'sync.php' -execdir php {} \;
O procedimento acima executará cada um dos Replicator.php
scripts primeiro (um após o outro, não em paralelo) e, quando terminar, executará cada um dos sync.php
scripts.
Responder3
{ cat > sync.sh
ln -s sync.sh replicator.sh
chmod +x sync.sh
} <<""
#!/bin/sh
cmd=${0%.*}
cmd=${cmd##*/}
find /var/sync -name "$cmd.php" -exec /usr/bin/php {} \;
Portanto, quando você chama um script de shell, o shell define o parâmetro especial do shell $0
para o nome do script que lê, como um programa C definiria argv[0]
. Você já está na metade do caminho e está executando dois scripts, cada um nomeado de acordo com seu destino de wrap. Mas você não precisa de dois scripts se tiver dois nomes - então você pode simplesmente vincular simbolicamente o mesmo script ao segundo nome e consultá-lo $0
quando chamá-lo.
Pois cron
você precisa apenas
i=-30
for s in sync replicator
do echo "0 $((i+=30)) * * * /var/sync/$s.sh" | crontab
done