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.php
und in jedem replicator
Verzeichnis gibt es ein Skript mit dem Namen Replicator.php
. Ich muss diese Skripte jede Nacht ausführen. Sie Replicator.php
sollten um 00:00 Uhr und sync.php
um 00:30 Uhr ausgeführt werden. Ich weiß, dass der richtige Pfad hier offensichtlich ein Cron-Job ist. Ich habe zwei Skripte erstellt replicator.sh
und sync.sh
verwende 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 30
oder ähnlich. Beachten Sie, dass einige Verzeichnisse nach einer Änderung übersprungen werden könnten, da die Dateien alphabetisch sortiert sind.
run.sh
sollte nur
dir=$1
script=$2
if [[ -f "$dir$script" ]] ; then
cd "$dir"
/usr/bin/php $script
fi
Antwort2
Der einfachste Ansatz wären zwei find
Befehle:
find /var/sync/ -name 'sync.php' -execdir php {} \;
find /var/sync/ -name 'Replicator.php' -execdir php {} \;
sync.php
Dadurch 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 Replicator
Skripte 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.php
Skript gleichzeitig ausgeführt wird und ii) jedes sync
Skript erst ausgeführt wird, nachdem das entsprechende replicator
Skript 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.php
zuerst 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 $0
auf 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, $0
wenn Sie es aufrufen.
Denn cron
du brauchst nur
i=-30
for s in sync replicator
do echo "0 $((i+=30)) * * * /var/sync/$s.sh" | crontab
done