Прочитать каждый каталог и выполнить действия в скрипте Bash

Прочитать каждый каталог и выполнить действия в скрипте Bash

У меня такая структура каталогов /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

Как вы видите, в каждом sync_*каталоге есть скрипт с именем sync.php, и в каждом replicatorкаталоге есть скрипт с именем Replicator.php. Мне нужно запускать эти скрипты каждую ночь. Replicator.phpдолжен запускаться в 00:00 утра и sync.phpдолжен запускаться в 12:30 ночи. Я знаю, что правильный путь здесь, очевидно, cron job. Я создал два скрипта replicator.shи sync.shразделяю почти один и тот же код, главное отличие — это имя скрипта. Смотрите код ниже:

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

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

Затем я добавил каждую из них в cronjob следующим образом:

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/

Моя идея, лежащая в основе всего этого объяснения, заключается в оптимизации моей среды путем изменения скрипта на один, который позволит мне выполнять все действия всего за один вызов cronjob и сохранять меньше файлов для поддержки. Поэтому я провел небольшое исследование и нашел полезные темы:

  • Использование find some/dir -type f -execdir somecommand {} \;- это правильный способ достичь того же, что я делаю с помощьюcd
  • И перейдя в каждый каталог по корневому пути - в моем случае/var/sync

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

Это псевдокод того, о чем я говорю (на случай, если это недостаточно понятно):

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

Я не настолько эксперт в bash, поэтому прошу помочь перевести этот псевдокод во что-то функциональное, что позволит мне достичь того, что я ищу. Есть ли у вас какие-либо советы и/или помощь?

ОБНОВЛЯТЬ

Возможно, я ошибаюсь, но я вообще не понимаю, что вы пытаетесь сделать, поэтому вот порядок событий:

# 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

Имея эту информацию (и извините, что не разъяснил это в первую очередь), как ваши решения должны работать? Как они у вас есть сейчас или вам нужно что-то изменить, чтобы это охватить?

решение1

Это переведет ваш псевдокод в 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

Он запускает sync.php и replicator.php одновременно, хотя изначально вы хотели вызывать их в разное время. Может быть, что-то вроде

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

вызывается из cron как

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

${run##*/}удаляет все до последнего /, т.е. возвращает только базовое имя.

Обновлять

Если вы хотите запустить скрипты следующим образом:

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
...

и т. д., вы можете просто перегенерировать crontab каждый раз при изменении структуры каталогов:

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

Назвать как generate_crontab.sh 30или подобное. Обратите внимание, что некоторые каталоги могут быть пропущены после изменения, так как файлы сортируются в алфавитном порядке.

run.shдолжен просто

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

решение2

Самый простой подход — две findкоманды:

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

Это будет искать файлы с именами sync.phpили Replicator.php, затем cd в их родительский каталог и выполнять их с помощью php. Вы можете добавить команды непосредственно в свой crontab:

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

Если вам нужно, Replicatorчтобы скрипты запускались с 30-минутной паузой между ними, вы можете сделать что-то вроде:

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

Это сначала запустит скрипт, затем подождет 30 минут, прежде чем перейти к следующему.

Наконец, если вам нужно убедиться, что i) у вас никогда не будет sync.phpзапущено более 1 скрипта одновременно и ii) каждый syncскрипт запускается после replicatorзавершения соответствующего скрипта, вы можете использовать это в качестве команды, которую вы даете cron:

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

Приведенный выше код сначала запустит каждый из Replicator.phpсценариев (один за другим, а не параллельно), а затем, как только они будут завершены, запустит каждый из sync.phpсценариев.

решение3

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

Итак, когда вы вызываете скрипт оболочки, оболочка устанавливает специальный параметр оболочки $0на имя скрипта, который она читает, как это сделала бы программа на языке C. argv[0]Вы уже на полпути, поскольку запускаете два скрипта, каждый из которых назван по своей цели wrap. Но вам не нужны два скрипта, если у вас два имени, поэтому вы можете просто создать символическую ссылку на тот же скрипт со вторым именем и ссылаться на $0него при вызове.

Для cronвас нужно только

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

Связанный контент