Lea cada directorio y realice acciones en el script Bash

Lea cada directorio y realice acciones en el script Bash

Tengo esta estructura de directorios en /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 puede ver, en cada sync_*directorio hay un script llamado sync.phpy en cada replicatordirectorio hay un script llamado Replicator.php. Necesito ejecutar esos scripts cada noche. Replicator.phpdebe ejecutarse a las 00:00 a. m. y sync.phpdebe ejecutarse a las 00:30 a. m. Sé que el camino correcto aquí es obviamente un trabajo cron. He creado dos scripts replicator.shy sync.shcomparto casi el mismo código, la principal diferencia es el nombre del script. Ver código a continuación:

replicator.sh
#! /bin/bash
cd $1
/usr/bin/php Replicator.php

sync.sh
#! /bin/bash
cd $1
/usr/bin/php sync.php

Luego agregué cada uno a cronjob de la siguiente manera:

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/

Mi idea detrás de toda esta explicación es optimizar mi entorno cambiando el script a uno que me permita realizar todas las acciones en una sola llamada cronjob y mantener menos archivos que mantener. Así que investigué un poco y encontré temas útiles:

  • Usar find some/dir -type f -execdir somecommand {} \;es una forma adecuada de lograr lo mismo que estoy haciendo concd
  • Y moviéndose a cada directorio bajo la ruta raíz, en mi caso/var/sync

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

Este es un pseudocódigo de lo que estoy hablando (en caso de que no esté lo suficientemente claro):

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

No soy tan experto en bash así que pido ayuda para traducir ese pseudocódigo en algo funcional que me permita lograr lo que busco, ¿algún consejo y/o ayuda?

ACTUALIZAR

Quizás me equivoque, pero no sigo en absoluto lo que ustedes intentan hacer, así que aquí está el orden de los 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

Teniendo esa información (y perdón por no aclararla primero), ¿cómo deberían funcionar sus soluciones? ¿Como los tienes ahora o necesitas cambiar algo para cubrir esto?

Respuesta1

Esto traduce tu pseudocódigo a 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

Sin embargo, ejecuta sync.php y replicator.php al mismo tiempo, pero originalmente querías llamarlos en momentos diferentes. Tal vez 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

llamado desde cron como

0 30 * * * /path/to/script.sh sync.php
0 0 * * * /path/to/script.sh replicator/Replicator.php

${run##*/}elimina todo lo anterior al último /, es decir, devuelve solo el nombre base.

Actualizar

Si desea ejecutar los scripts de la siguiente manera:

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., puedes simplemente regenerar el crontab cada vez que cambies la estructura del directorio:

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

Llame como generate_crontab.sh 30o similar. Tenga en cuenta que algunos de los directorios podrían omitirse después de un cambio, ya que los archivos están ordenados alfabéticamente.

run.shdebería simplemente

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

Respuesta2

El enfoque más simple sería dos findcomandos:

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

Eso buscará archivos llamados sync.phpo Replicator.php, luego cd en su directorio principal y los ejecutará con php. Podrías agregar los comandos directamente a tu crontab:

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

Si necesita que los Replicatorscripts se ejecuten con una pausa de 30 minutos entre ellos, puede hacer algo como:

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

Primero se ejecutará el script y luego se esperarán 30 minutos antes de pasar al siguiente.

Finalmente, si necesita asegurarse de que i) nunca tenga >1 sync.phpejecutándose al mismo tiempo y ii) cada syncsecuencia de comandos se ejecute después de que finalice la secuencia de comandos correspondiente replicator, puede usar esto como el comando que le da a cron:

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

Lo anterior ejecutará cada uno de los Replicator.phpscripts primero (uno tras otro, no en paralelo) y, una vez que hayan terminado, ejecutará cada uno de los sync.phpscripts.

Respuesta3

{   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 {} \;

Entonces, cuando llama a un script de shell, el shell establece el parámetro de shell especial con $0el nombre del script que lee, como lo haría un programa en C. argv[0]Ya está a mitad de camino porque está ejecutando dos scripts, cada uno con el nombre de su objetivo de ajuste. Pero no necesita dos scripts si tiene dos nombres, por lo que puede simplemente vincular simbólicamente el mismo script al segundo nombre y consultarlo $0cuando lo llame.

Porque cronsolo necesitas

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

información relacionada