各ディレクトリを読み取り、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 AM に実行し、 はsync.php12:30 AM に実行する必要があります。ここでの正しいパスは明らかに cron ジョブであることがわかっています。 2 つのスクリプトを作成しました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/

この説明の背後にある私の考えは、スクリプトを 1 つだけに変更して環境を最適化することです。これにより、すべてのアクションを 1 回の 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

最も簡単な方法は、次の 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 分間待機してから次のスクリプトに進みます。

sync.php最後に、i)同時に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 ターゲットにちなんで名付けられた 2 つのスクリプトを実行しているので、すでに半分の段階に入っています。 ただし、名前が 2 つある場合は、2 つのスクリプトは必要ありません。つまり、同じスクリプトを 2 番目の名前にシンボリック リンクし、$0それを呼び出すときに を参照するだけで済みます。

あなたにcron必要なのは

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

関連情報