Ich habe ein Shell-Scripting-Problem, bei dem ich ein Verzeichnis voller Eingabedateien (jede Datei enthält viele Eingabezeilen) bekomme, und ich muss sie einzeln verarbeiten und jede ihrer Ausgaben in eine eindeutige Datei umleiten (auch bekannt als file_1.input-Anforderungen) in file_1.output zu erfassen, und so weiter).
Vor der Parallelisierung durchlieferte ich einfach jede Datei im Verzeichnis und führte meinen Befehl aus, während ich eine Art Timer- / Zähltechnik ausführte, um die Prozessoren nicht zu überfordern (vorausgesetzt, jeder Prozess hatte eine konstante Laufzeit). Ich weiß jedoch, dass dies nicht immer der Fall sein wird. Daher scheint die Verwendung einer "parallelen" Lösung der beste Weg zu sein, um das Multithreading von Shell-Skripten zu erreichen, ohne benutzerdefinierten Code zu schreiben.
Obwohl ich mir überlegt habe, wie ich jede dieser Dateien parallel verarbeiten kann (und meine Kerne effizient verwalten kann), scheinen sie alle hacky zu sein. Ich halte das für einen ziemlich einfachen Anwendungsfall und würde es daher vorziehen, es so sauber wie möglich zu halten (und nichts in den parallelen Beispielen scheint als mein Problem herauszuspringen.
Jede Hilfe wäre dankbar!
Beispiel für ein Eingabeverzeichnis:
> ls -l input_files/
total 13355
location1.txt
location2.txt
location3.txt
location4.txt
location5.txt
Skript:
> cat proces_script.sh
#!/bin/sh
customScript -c 33 -I -file [inputFile] -a -v 55 > [outputFile]
Update : Nachdem ich Ole's Antwort unten gelesen hatte, konnte ich die fehlenden Teile für meine eigene parallele Implementierung zusammenstellen. Während seine Antwort großartig ist, sind hier meine zusätzlichen Nachforschungen und Notizen, die ich gemacht habe:
Anstatt meinen gesamten Prozess auszuführen, begann ich mit einem Proof-of-Concept-Befehl, um seine Lösung in meiner Umgebung zu beweisen. Siehe meine zwei verschiedenen Implementierungen (und Hinweise):
find /home/me/input_files -type f -name *.txt | parallel cat /home/me/input_files/{} '>' /home/me/output_files/{.}.out
Verwendet find (nicht ls, das kann Probleme verursachen), um alle zutreffenden Dateien in meinem Eingabedateiverzeichnis zu finden und leitet ihren Inhalt dann in ein separates Verzeichnis und eine separate Datei um. Mein Problem von oben war das Lesen und Umleiten (das eigentliche Skript war einfach), daher war das Ersetzen des Skripts durch cat ein guter Proof of Concept.
parallel cat '>' /home/me/output_files/{.}.out ::: /home/me/input_files/*
Diese zweite Lösung verwendet das Eingabevariablen-Paradigma von parallel, um die Dateien einzulesen. Für Anfänger war dies jedoch viel verwirrender. Mit find a and pipe habe ich meine Bedürfnisse bestens erfüllt.
quelle
Die Standardmethode besteht darin, eine Warteschlange einzurichten und eine beliebige Anzahl von Mitarbeitern zu erzeugen, die wissen, wie sie etwas aus der Warteschlange ziehen und verarbeiten können. Sie können ein FIFO (auch als Named Pipe bezeichnet) für die Kommunikation zwischen diesen Prozessen verwenden.
Nachfolgend finden Sie ein naives Beispiel zur Veranschaulichung des Konzepts.
Ein einfaches Warteschlangenskript:
Und ein Arbeiter:
process_file
könnte irgendwo in Ihrem Arbeiter definiert sein und es kann tun, was immer Sie es tun müssen.Sobald Sie diese beiden Teile haben, können Sie über einen einfachen Monitor verfügen, der den Warteschlangenprozess und eine beliebige Anzahl von Arbeitsprozessen startet.
Überwachen Sie Skript:
Hier hast du es. Wenn Sie dies tatsächlich tun, ist es besser, das FIFO im Monitor einzurichten und den Pfad sowohl an die Warteschlange als auch an die Worker zu übergeben, damit sie nicht gekoppelt sind und nicht an einen bestimmten Ort für das FIFO gebunden sind. Ich habe es in der Antwort so eingestellt, damit klar ist, was Sie verwenden, während Sie es lesen.
quelle
monitor_workers
ist wieprocess_file
- es ist eine Funktion, die macht, was Sie wollen. Über den Monitor - Sie hatten Recht; es sollte die Pids seiner Arbeiter speichern (damit es ein Kill-Signal senden kann) und der Zähler muss erhöht werden, wenn es einen Arbeiter startet. Ich habe die Antwort so bearbeitet, dass sie diese enthält.parallel
. Ich denke, es ist Ihre Idee, vollständig umgesetzt.Ein anderes Beispiel:
Ich fand die anderen Beispiele unnötig komplex, obwohl Sie in den meisten Fällen nach den obigen Beispielen gesucht haben.
quelle
Ein allgemein verfügbares Werkzeug, das Parallelisierung durchführen kann, ist make. GNU make und einige andere haben die
-j
Option, parallele Builds durchzuführen.Laufen Sie
make
so (ich nehme an, dass Ihre Dateinamen keine Sonderzeichen enthalten,make
ist nicht gut mit denen):quelle
So führen Sie denselben Befehl für eine große Anzahl von Dateien im aktuellen Verzeichnis aus:
Dadurch wird die Datei
customScript
für jedetxt
Datei ausgeführt und die Ausgabe inouttxt
Dateien abgelegt. Ändern Sie nach Bedarf. Der Schlüssel, um dies zum Laufen zu bringen, ist die Signalverarbeitung mit SIGUSR1, damit der untergeordnete Prozess dem übergeordneten Prozess mitteilt, dass dies geschehen ist. Die Verwendung von SIGCHLD funktioniert nicht, da die meisten Anweisungen im Skript SIGCHLD-Signale für das Shell-Skript generieren. Ich habe versucht, diesen Befehl durch zu ersetzensleep 1
. Das Programm verwendete 0,28 s Benutzer-CPU und 0,14 s System-CPU. Dies war nur auf etwa 400 Dateien.quelle
wait
, was schlau genug ist; aber es wird nach Erhalt desSIGUSR1
Signals zurückkehren. Das Kind / der Arbeiter sendet einSIGUSR1
an das Elternteil, das abgefangen (trap
) wird, und dekrementiert$worker
(trap
Klausel) und kehrt abnormal zurückwait
, wodurch dieif [ $worker -lt $num_workers ]
Klausel ausgeführt werden kann.Oder verwenden Sie einfach
xargs -P
, ohne zusätzliche Software installieren zu müssen:Ein bisschen Erklärung für die Optionen:
-I'XXX'
Legt die Zeichenfolge fest, die in der Befehlsvorlage durch den Dateinamen ersetzt wird-P4
führt 4 Prozesse parallel aus-n1
legt nur eine Datei pro Ausführung ab, obwohl zwei XXX gefunden werden-print0
und-0
arbeiten Sie zusammen, sodass Sie Sonderzeichen (wie Leerzeichen) in den Dateinamen habenquelle