Abfrage zum parallelen Ausführen von For-Loop-Skripten

Abfrage zum parallelen Ausführen von For-Loop-Skripten

Ich habe folgendes in einem Shell-Skript:

for file in $local_dir/myfile.log.*; 
    do 
        file_name=$(basename $file); 
        server_name=$(echo $file_name | cut -f 3 -d '.');
        file_location=$(echo $file);

        mv $file_location $local_dir/in_progress1.log

        mysql -hxxx -P3306 -uxxx -pxxx -e "set @server_name='${server_name}'; source ${sql_script};"

        rm $local_dir/in_progress1.log
    done

Es ruft grundsätzlich alle Dateien in einem Verzeichnis ab, die den Kriterien entsprechen, extrahiert einen Servernamen aus dem Dateinamen und übergibt ihn zur Verarbeitung an ein MySQL-Skript.

Ich frage mich, ob ich 10 Dateien habe, die jeweils 60 Sekunden zum Fertigstellen benötigen, und nach 5 Minuten dann eine zweite Instanz des Shell-Skripts starte:

  • a) sieht das zweite Skript immer noch die Dateien, die nicht verarbeitet wurden?
  • b) verursacht es beim ersten Mal Probleme, wenn Dateien gelöscht werden

oder kann ich sie problemlos parallel ausführen?

Antwort1

Man könnte annehmen, dass „60 Sekunden“ (und sogar „5 Minuten“) nur eine gute Schätzung ist und dass das Risiko besteht, dass der erste Stapel noch in Bearbeitung ist, wenn der zweite Stapel gestartet wird. Wenn Sie die Stapel trennen möchten (und wenn es abgesehen von den Protokolldateien bei einer gelegentlichen Überlappung kein Problem gibt), wäre es besser, eine Stapelnummer als Teil der Benennungskonvention für laufende Dateien festzulegen.

Etwas wie das:

[[ -s ]] $local_dir/batch || echo 0 > $local_dir/batch
batch=$(echo $local_dir/batch)
expr $batch + 1 >$local_dir/batch

Überprüfen Sie vor der For-Schleife und dann am Anfang der Schleife, ob Ihr Muster mit einer tatsächlichen Datei übereinstimmt

[[ -f "$file" ]] || continue

und verwenden Sie die Batchnummer im Dateinamen:

mv $file_location $local_dir/in_progress$batch.log

und vorwärts. Das verringert die Kollisionsgefahr.

Antwort2

Oben gibt es eine Antwort, die einige gute Lösungen für das Problem bietet, aber ich dachte, ich würde ein wenig Erklärung zu denWarumwas das Problem ist.

Im Großen und Ganzen gilt: Solange Ihre umbenannten Logdateien (die in Bearbeitung befindlichen) die Kriterien nicht erfüllen, sind Siewahrscheinlichsicher, dies auszuführen mitminimalRisiko. Sie werden trotzdem einige Fehler erhalten ...

Ihre Dateiliste wird beim Ausführen des Skripts generiert. Das würde also letztendlich passieren:

Script Aruft eine Liste von ab 10 files. Beginnt mit der Verarbeitung, 5 filesin (5 verbleibend) script Bruft eine Liste von ab 5 remaining files, beginnt mit der Verarbeitung. Script ageht dann zur Verarbeitung der nächsten Datei auf seiner Liste über (die dieselbe ist, deren script BVerarbeitung begonnen hat). Es wird ein Fehler auftreten, weil die Datei umbenannt wurde. Mit der Fehlerbehandlung könnte es also theoretisch zur nächsten in seiner Liste übergehen und problemlos funktionieren. Aber natürlich besteht IMMER die Möglichkeit, dass die Sterne günstig stehen, aber Skripte gleichzeitig auf dieselbe Datei stoßen und etwas Unerwartetes passiert. Wägen Sie dieses Risiko ab, wie Sie wollen.

Eine möglicherweise elegantere Lösung wäre, dies in ein pythonSkript umzuwandeln und zu prüfen parallel for loops, wie Sie eine einzelne For-Schleife erstellen und diese parallel ausführen können, sodass ein Skript die Arbeit von zwei oder mehr Skripten erledigen kann.

Antwort3

Eine andere Möglichkeit besteht darin, eine einfache Batch-Warteschlange in Ihrem Skript zu implementieren.

Zu Beginn des Skripts könnten Sie etwa Folgendes tun:

mkdir -p $localdir/batch
BATCHTMP=$(mktemp batch.XXXXXXXXXX)
MYBATCH="$localdir/batch/batch.$$"

# get list of current log files
find $local_dir/ -name 'myfile.log.*' > "$BATCHTMP"

# exclude any log files already in other batches
grep -vF -f <(sort -u $localdir/batch/batch.*) < "$BATCHTMP" > "$MYBATCH"

rm -f "$BATCHTMP"

# only process log files that are in my batch
for lf in $(cat "$MYBATCH") ; do
....
# somewhere in here, mv or rm the logfile being processed
# so it doesn't get processed again in a later batch run
done

rm -f "$MYBATCH"

Dies ist natürlich nur ein einfacher Überblick über die zu erledigenden Aufgaben.

Dies könnte übrigens auch in einem Wrapper-Skript erfolgen, das nichts anderes tut, als die Batchdatei zu generieren und dann das Hauptskript auszuführen.

verwandte Informationen