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

2

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 diretório sync_* , existe um script chamado sync.php e em cada diretório replicator existe um script chamado Replicator.php . Eu preciso executar esses scripts a cada noite. Replicator.php deve ser executado às 00:00 e sync.php deve ser executado às 12:30. Eu sei que o caminho certo aqui é obviamente um trabalho cron. Eu fiz dois scripts replicator.sh e sync.sh compartilhando 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

Depois, adicionei cada um ao cronjob da seguinte forma:

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 é toda a explicação para otimizar o meu ambiente, alterando o script para apenas um que me permita realizar todas as ações em apenas uma chamada do cronjob e manter menos arquivos para manter. Então eu fiz uma pequena pesquisa e encontrei tópicos úteis:

  • Usar find some/dir -type f -execdir somecommand {} \; é uma maneira correta de alcançar a mesma coisa que eu estou fazendo com cd
  • E mudando para cada diretório sob o caminho da raiz - no meu caso /var/sync

     for D in ./*; do
        if [ -d "$D" ]; then
            cd "$D"
            run_something
            cd ..
        fi
    done
    

Este é um pseudo có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

Eu não sou tão bom especialista, então estou pedindo alguma ajuda para traduzir esse pseudo código em algo funcional que me permita alcançar o que estou procurando, algum conselho e / ou ajuda?

UPDATE

Talvez eu esteja errado, mas não estou acompanhando o que vocês estão tentando fazer, 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 em primeiro lugar) como suas soluções devem funcionar? Como você os tem agora ou precisa mudar alguma coisa para cobrir isso?

    
por ReynierPM 04.11.2015 / 15:18

3 respostas

2

A abordagem mais simples seria dois comandos find :

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

Isso irá procurar por arquivos chamados sync.php ou Replicator.php , depois cd no diretório pai e execute-os 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 você precisar que os scripts Replicator sejam executados com uma pausa de 30 minutos entre eles, faça algo como:

find /var/sync/ -name 'Replicator.php' -execdir php {} \; -exec sh -c "sleep 30m" \;

Isso primeiro executará o script e aguardará 30 minutos antes de passar para o próximo.

Por fim, se você precisar garantir que i) nunca tenha > 1 sync.php em execução ao mesmo tempo e ii) cada script sync seja executado após a conclusão do script replicator correspondente, use isso como o comando que você dá ao cron:

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

Os itens acima executarão cada um dos scripts Replicator.php primeiro (um após o outro, não em paralelo) e, assim que terminarem, executarão cada um dos scripts sync.php .

    
por 04.11.2015 / 16:04
3

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 roda o sync.php e o replicator.php ao mesmo tempo, mas você originalmente 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 de base.

Atualizar

Se você deseja executar os scripts da seguinte forma:

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 apenas regenerar o crontab toda vez que você alterar a estrutura do diretório:

#!/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 deve apenas

dir=$1
script=$2
if [[ -f "$dir$script" ]] ; then
    cd "$dir"
    /usr/bin/php $script
fi
    
por 04.11.2015 / 15:52
1
{   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ê chamar um script de shell, o shell configurará o parâmetro de shell especial $0 para o nome do script que ele lê, pois um programa em C definirá argv[0] . Você já está na metade do caminho e está executando dois scripts, cada um denominado pelo seu alvo de quebra. Mas você não precisa de dois scripts se tiver dois nomes - assim você pode apenas ligar simbolicamente o mesmo script ao segundo nome e se referir a $0 quando você o chamar.

Para cron você precisa apenas

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