Leia cada diretório e execute ações no script Bash

Leia cada diretório e execute ações no script Bash

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.phpe em cada replicatordiretório existe um script chamado Replicator.php. Preciso executar esses scripts todas as noites. Replicator.phpdeve ser executado às 00h00 e sync.phpàs 12h30. Eu sei que o caminho certo aqui é obviamente um cron job. Fiz dois scripts replicator.she sync.shcompartilhei 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 30ou 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.shdeveria apenas

dir=$1
script=$2
if [[ -f "$dir$script" ]] ; then
    cd "$dir"
    /usr/bin/php $script
fi

Responder2

A abordagem mais simples seria dois findcomandos:

find /var/sync/ -name 'sync.php' -execdir php {} \;
find /var/sync/ -name 'Replicator.php' -execdir php {} \;

Isso procurará por arquivos chamados sync.phpou 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 Replicatorscripts 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.phpem execução ao mesmo tempo e ii) cada script será executado após a conclusão do script synccorrespondente , 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.phpscripts primeiro (um após o outro, não em paralelo) e, quando terminar, executará cada um dos sync.phpscripts.

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 $0para 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 $0quando chamá-lo.

Pois cronvocê precisa apenas

i=-30
for  s in sync replicator
do   echo "0 $((i+=30)) * * * /var/sync/$s.sh" | crontab
done

informação relacionada