Lesen Sie jedes Verzeichnis und führen Sie Aktionen im Bash-Skript aus

Lesen Sie jedes Verzeichnis und führen Sie Aktionen im Bash-Skript aus

Ich habe diese Verzeichnisstruktur unter /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

Wie Sie sehen, sync_*gibt es in jedem Verzeichnis ein Skript mit dem Namen sync.phpund in jedem replicatorVerzeichnis gibt es ein Skript mit dem Namen Replicator.php. Ich muss diese Skripte jede Nacht ausführen. Sie Replicator.phpsollten um 00:00 Uhr und sync.phpum 00:30 Uhr ausgeführt werden. Ich weiß, dass der richtige Pfad hier offensichtlich ein Cron-Job ist. Ich habe zwei Skripte erstellt replicator.shund sync.shverwende fast denselben Code. Der Hauptunterschied ist der Skriptname. Siehe Code unten:

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

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

Dann habe ich jeweils Folgendes zum Cronjob hinzugefügt:

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/

Meine Idee hinter all diesen Erklärungen ist, meine Umgebung zu optimieren, indem ich das Skript auf ein einziges ändere, das es mir ermöglicht, alle Aktionen in nur einem Cronjob-Aufruf auszuführen und weniger Dateien zu verwalten. Also habe ich ein wenig recherchiert und ein paar hilfreiche Themen gefunden:

  • Durch die Verwendung von find some/dir -type f -execdir somecommand {} \;kann ich dasselbe erreichen wie mitcd
  • Und indem Sie in jedes Verzeichnis unter dem Stammpfad wechseln - in meinem Fall/var/sync

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

Dies ist ein Pseudocode dessen, worüber ich spreche (falls es nicht klar genug ist):

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

Ich bin kein großer Bash-Experte und bitte daher um Hilfe bei der Übersetzung dieses Pseudocodes in etwas Funktionales, mit dem ich das erreichen kann, was ich suche. Gibt es Ratschläge und/oder Hilfe?

AKTUALISIEREN

Vielleicht irre ich mich, aber ich verstehe überhaupt nicht, was Sie versuchen, also hier die Reihenfolge der Ereignisse:

# 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

Mit diesen Informationen (und entschuldigen Sie, dass ich das nicht gleich klargestellt habe), wie sollten Ihre Lösungen funktionieren? So wie Sie sie jetzt haben oder müssen Sie etwas ändern, um dies abzudecken?

Antwort1

Dies übersetzt Ihren Pseudocode in 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

Es führt sowohl sync.php als auch replicator.php gleichzeitig aus, obwohl Sie sie ursprünglich zu unterschiedlichen Zeiten aufrufen wollten. Vielleicht so etwas wie

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

wird von cron wie folgt aufgerufen

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

${run##*/}entfernt alles vor dem letzten /, d. h. gibt nur den Basisnamen zurück.

Aktualisieren

Wenn Sie die Skripte wie folgt ausführen möchten:

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

usw. können Sie die Crontab einfach jedes Mal neu generieren, wenn Sie die Verzeichnisstruktur ändern:

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

Aufruf als generate_crontab.sh 30oder ähnlich. Beachten Sie, dass einige Verzeichnisse nach einer Änderung übersprungen werden könnten, da die Dateien alphabetisch sortiert sind.

run.shsollte nur

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

Antwort2

Der einfachste Ansatz wären zwei findBefehle:

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

sync.phpDadurch werden Dateien mit den Namen oder gesucht Replicator.php, dann wird mit cd in das übergeordnete Verzeichnis gewechselt und die Dateien mit ausgeführt php. Sie können die Befehle direkt zu Ihrer Crontab hinzufügen:

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

Wenn die ReplicatorSkripte mit einer Pause von 30 Minuten ausgeführt werden müssen, können Sie Folgendes tun:

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

Dadurch wird zuerst das Skript ausgeführt. Anschließend wird 30 Minuten gewartet, bevor mit dem nächsten fortgefahren wird.

Wenn Sie schließlich sicherstellen müssen, dass i) nie mehr als 1 sync.phpSkript gleichzeitig ausgeführt wird und ii) jedes syncSkript erst ausgeführt wird, nachdem das entsprechende replicatorSkript beendet wurde, können Sie dies als Befehl an cron geben:

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

Das Obige führt Replicator.phpzuerst alle Skripte aus (nacheinander, nicht parallel) und führt sie nach Abschluss einzeln aus sync.php.

Antwort3

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

Wenn Sie also ein Shell-Skript aufrufen, setzt die Shell den speziellen Shell-Parameter $0auf den Namen des Skripts, das sie liest, so wie es ein C-Programm tun würde argv[0]. Sie haben es schon halb geschafft, da Sie zwei Skripts ausführen, die jeweils nach ihrem Wrap-Ziel benannt sind. Aber Sie brauchen nicht zwei Skripts, wenn Sie zwei Namen haben – Sie können also einfach dasselbe Skript mit dem zweiten Namen symbolisch verknüpfen und darauf verweisen, $0wenn Sie es aufrufen.

Denn crondu brauchst nur

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

verwandte Informationen