2016-07-01 23 views
2

Ich habe ein Skript, das eine Charge von 20MiB CSV-Dateien verarbeitet, optional gzip-komprimiert auf etwa 4MiB. Es gibt viele tausend Dateien und die Verarbeitung dauert jeweils etwa 30 Sekunden. Lesen entweder eine unkomprimierte Datei oder eine komprimierte Datei und Dekomprimierung ist "fast sofort", was stark darauf hindeutet, dass der Prozess auf der Prozessebene parallelisiert werden kann. In der Tat wird das mit einer komplexen Ruby-Pipeline gemacht. Ich versuche jedoch, den Ruby-Code mit Bash in kleinere Teile zu zerlegen. Für Jobsteuerung, würde ich mit dieser bash FunktionBash Prozess Substitution Hintergrund mit Auftragssteuerung

wait_until_job_available() { 
    maximum_jobs=${MAXIMUM_JOBS} 
    [ $# -eq 0 ] || maximum_jobs="${1}" 
    exit_status=0 
    RUNNING_JOBS=($(jobs -p)) 
    while [ ${maximum_jobs} -le ${#RUNNING_JOBS[@]} ] && [ 0 -eq "${exit_status}" ] 
    do 
     # `wait -n` requires bash 4.3 which is unfortunately not available on several recent RHEL-based Linux distributions such as Oracle Linux 
     wait -n 
     exit_status=$? 
     RUNNING_JOBS=($(jobs -p)) 
    done 
    return ${exit_status} 
} 

Dies ermöglicht es mir wait_until_job_available zu nennen kommen, mit einer optionalen Mindestzahl von Arbeitsplätzen laufen gelassen (wenn weggelassen, wird standardmäßig die Anzahl der Kerne auf dem zur Verfügung stehenden Maschine), vor dem Hintergrund einer Bash-Pipeline.

So könnte ich es verwenden, wie solche:

while read file 
do 
    CAT_COMMAND=cat 

    # if input file is gzip-compressed, pipe zcat instead of cat 
    if [ "${INFILE: -3}" == ".gz" ] 
    then 
     CAT_COMMAND=zcat 
    fi 

    # wait for a job to become available 
    wait_until_job_available 

    # read the uncompressed file, write processed data to file.out 
    process_file -i <(${CAT_COMMAND} ${file}) -o ${file}.out & 

# while searching for filesystem paths of type _f_ile 
done < <(find ${search_path} -type f) 

# wait for all background jobs to finish 
wait 

Wie Sie sehen können, das alle Dateien innerhalb search_path und passieren, dass an den process_file Befehl finden sollte. Dabei verwende ich die Prozesssubstitution, um entweder die Datei zu katalysieren oder die Datei während der Übertragung zu dekomprimieren. Der Eingabedateiname wird durch einen Prozess ersetzt, der den Inhalt der unkomprimierten Datei ausgibt, und die Ausgabedatei ist der ursprüngliche Dateiname mit angehängtem ".out". Der Aufruf von process_file wird im Hintergrund ausgeführt und an die Jobsteuerung gesendet. Sieht gut aus, oder?

Außer dass ich festgestellt habe, dass einige Dateien nicht richtig behandelt werden.

Ich bemerkte, dass die Datei, die von process_file verarbeitet wird, immer /dev/fd/63 gemeldet wird, auch für separate gleichzeitige Instanzen von process_file. Wenn ich die Datei separat in eine temporäre Datei kopiere oder dekomprimiere und den Namen der temporären Datei an process_file übergebe, wird die Ausführung normal ausgeführt und alle Dateien werden korrekt verarbeitet.

Ich wollte vermeiden, eine temporäre Datei zu erstellen, insbesondere in Bezug auf das Berühren der Festplatte (Leistung) und muss die temporäre Datei nach der Verarbeitung aufräumen (entfernen); dieses Problem zu verhindern, dass. Also bin ich neugierig, ob es eine Art Race-Bedingung für den Namen des Pseudofiles für die ersetzte Prozesspipeline gibt? Oder gibt es etwas über Prozesssubstitution oder Jobkontrolle, das ich falsch verstehe?

Als Referenz Ich verwende Ubuntu Server 14.04, Linux 3.19.0-59 Bash 4.3.11 gzip 1.6

Antwort

1

habe ich ein bisschen graben und vielleicht Sie weisen in die richtige Lage sein, Richtung.

Anscheinend ist/dev/fd/63 ein Standarddateideskriptor, der von process_file verwendet wird. Wenn Sie also mehrere Instanzen von process_file ausführen, versucht es, alles über diesen Dateideskriptor zu senden. Sie erstellen wahrscheinlich einen Konflikt oder eine Wettlaufsituation, wenn Sie vermuten. Diese Seite file descriptors and bash shell scripting und diese Seite redirection_tutorial haben Beispiele zum Umleiten von Ausgängen.

Wahrscheinlich müssen Sie process_file ändern, um eindeutige Dateideskriptoren zu erstellen oder den Deskriptor während seiner Verwendung zu sperren.

+0

Aus meinem Verständnis wird der Dateideskriptor von Bash obwohl erstellt – inetknght