Eine while-Schleife und ein Here-Dokument - was passiert wann?

7

Ich habe diese Kombination aus while-Schleife und hier-Dokument, die ich in Bash 4.3.48 (1) ausführe, und ich verstehe ihre Logik überhaupt nicht.

while read file; do source ~/unwe/"$file"
done <<-EOF
    x.sh
    y.sh
EOF

Meine Frage besteht aus folgenden Teilen:

  1. Was macht das readhier (ich readdeklariere eine Variable immer und weise ihren Wert interaktiv zu , aber mir fehlt, was sie hier tun soll).

  2. Was bedeutet das while read? Woher kommt das Konzept whilehier?

  3. Wenn das Here-Dokument selbst nach der Schleife kommt, wie wird es überhaupt von der Schleife beeinflusst? Ich meine, es kommt danach doneund nicht innerhalb der Schleife. Wie ist also die tatsächliche Assoziation zwischen diesen beiden Strukturen?

  4. Warum scheitert das?

    while read file; do source ~/unwe/"$file" done <<-EOF
        x.sh
        y.sh
    EOF

    Ich meine, doneist done... Warum ist es wichtig, wenn done <<-EOFes sich in derselben Zeile wie die Schleife befindet? Wenn ich mich richtig erinnere, hatte ich einen Fall, in dem eine forSchleife einzeilig war und immer noch funktionierte.

user9303970
quelle

Antworten:

12
  1. Der readBefehl liest aus seinem Standardeingabestream und weist der Variablen zu, was gelesen wird file(es ist etwas komplizierter, siehe lange Diskussion hier ). Der Standardeingabestream stammt aus dem Here-Dokument, das nachher in die Schleife umgeleitet wird done. Wenn keine Daten von irgendwoher angegeben werden , werden diese interaktiv vom Terminal gelesen. In diesem Fall hat die Shell jedoch angeordnet, ihren Eingabestream mit dem Here-Dokument zu verbinden.

  2. while readbewirkt, dass die Schleife iteriert, bis der readBefehl einen Exit-Status ungleich Null zurückgibt. Dies geschieht, wenn Fehler auftreten oder (am häufigsten) wenn keine Daten mehr gelesen werden müssen (der Eingabestream befindet sich am Ende der Datei).

    Die Konvention ist, dass jedes Dienstprogramm, das der aufrufenden Shell einen Fehler oder "falsch" oder "nein" signalisieren möchte, dies tut, indem es einen Exit-Status ungleich Null zurückgibt. Ein Null-Exit-Status signalisiert "true" oder "yes" oder "no error". Dieser Status, den Sie überprüfen möchten, ist in verfügbar $?(nur vom zuletzt ausgeführten Dienstprogramm). Der Exit-Status kann in ifAnweisungen und whileSchleifen oder überall dort verwendet werden, wo ein Test erforderlich ist. Zum Beispiel

    if grep -q 'pattern' file; then ...; fi
    
  3. Ein Here-Dokument ist eine Form der Umleitung . In diesem Fall handelt es sich um eine Umleitung in die Schleife. Alles in der Schleife könnte daraus lesen, aber in diesem Fall ist es nur der readBefehl, der dies tut. Lesen Sie hier die Dokumente. Wenn die Eingabe aus einer normalen Datei stammte, wäre die letzte Zeile gewesen

    done <filename
    

    Wenn Sie die Schleife als einen einzigen Befehl betrachten, ist dies möglicherweise intuitiver:

    while ...; do ...; done <filename
    

    Das ist ein Fall von

    somecommand <filename
    

    Einige Shells unterstützen auch "Here-Strings" mit <<<"string":

    cat <<<"This is the here-string"
    

    DavidFoerster weist darauf hin, dass, wenn eines der beiden Skripte x.shund y.shLesevorgänge von der Standardeingabe, ohne explizit Daten zum Lesen aus einer Datei oder von einem anderen Ort zu erhalten, die gelesenen Daten tatsächlich aus dem hier gezeigten Dokument stammen.

    Bei einem x.sh, der nur enthält read a, würde die Variable adie Zeichenfolge enthalten y.sh, und das y.shSkript würde niemals ausgeführt. Dies liegt an der Tatsache, dass die Standardeingabe für alle Befehle in der whileSchleife umgeleitet wird (und auch von jedem aufgerufenen Skript oder Befehl "geerbt" wird) und die zweite Zeile "verbraucht" wird, x.shbevor die whileSchleife readsie lesen kann.

    Wenn dieses Verhalten unerwünscht ist, kann es vermieden werden, ist aber etwas schwierig .

  4. Es schlägt fehl, weil es ;vorher keine oder eine neue Zeile gibt done. Ohne ;oder ohne Zeilenumbruch donewird das Wort doneals Argument von genommen sourceund die Schleife wird außerdem nicht richtig geschlossen (dies ist ein Syntaxfehler).

    Es ist fast wahr, dass jede ;durch eine neue Zeile ersetzt werden kann (zumindest wenn es sich um ein Befehlsbegrenzer handelt). Es signalisiert das Ende eines Befehls, wie auch |, &, &&und ||(und wahrscheinlich auch andere , die ich vergessen habe).

Kusalananda
quelle
3
+1 und ein Nitpick: "Alles in der Schleife könnte daraus lesen, aber in diesem Fall ist es nur der readBefehl, der dies tut." - Einige oder alle Shell-Skripte (und ihre rekursiv aufgerufenen Befehle) können aus der Standardeingabe lesen und die Schleifenaufrufe ändern.
David Foerster
@ DavidFoerster Das ist richtig. Ich war ein bisschen blind für das, was der Code tatsächlich tat. Ich werde bald etwas dazu hinzufügen.
Kusalananda
In diesem Fall ist es ziemlich einfach, Probleme mit anderen Dingen zu vermeiden, die von stdin gelesen werden. Übergeben Sie einfach das Here-Dokument auf Einheit 3 ​​anstelle von stdin ( ... done 3<<-EOF) und lassen Sie den readBefehl aus Einheit 3 ​​( while read file <&3; do ...) lesen .
Gordon Davisson
1
@GordonDavisson Ja, es ist einfach, abhängig von Ihrem Fachwissen und Ihrem Selbstvertrauen. Nach mehr als 25 Jahren an der Eingabeaufforderung kann ich mir immer noch nur ein paar Mal vorstellen (wenn das so ist), dass ich so mit Dateideskriptoren jonglieren musste, und ich werde es immer nachschlagen müssen :-)
Kusalananda
1

Sie erstellen tatsächlich ein Objekt als Dokument und leiten es dann zurück in eine while-Schleife, die es Zeile für Zeile liest, bis keine Zeilen mehr vorhanden sind, und führen einen Quellbefehl in dieser Zeile aus.

Eine andere Möglichkeit, es auszuführen, ist wie folgt:

Angenommen, die Linien

x.sh
y.sh

waren in einer Datei namens:

input.txt

Wir könnten ausführen

cat input.txt | while read file
   do source ~/unwe/"$file"
done

Das hier gezeigte Dokument macht die Dinge interaktiver, aber das obige Beispiel kann es Ihnen ermöglichen, es besser zu verstehen.

Raman Sailopal
quelle
1
Diese andere Methode entspricht nicht dem Original, da die Beschaffung in einer Subshell erfolgen kann.
Michael Homer