Wie stelle ich der Programmausgabe eine Zeichenfolge voran, ohne auf die ganze Zeile zu warten?

Wie stelle ich der Programmausgabe eine Zeichenfolge voran, ohne auf die ganze Zeile zu warten?

Ich habe ein Skript, das einen Befehl auf einem Remote-Server über SSH ausführt. Ich möchte die Zeichenfolge jeder Zeile der Ausgabe voranstellen Remote:, aber ich möchte nicht, dass jede Zeile verzögert wird, bis die gesamte Zeile verfügbar ist. Hier ist die Ausgabe meines Befehls:

$ myproject-db-push mein_Datenbankname
Exportieren aus der Datenbank... Fertig
Daten archivieren... Fertig
Archiv wird auf Remote-Speicher hochgeladen... Fertig
Ausführen des Installationsskripts auf der Remote-
Remote: Archiv in temporäres Verzeichnis dekomprimieren... Fertig
Remote: Verwendete Datenbank: my_database_name
Remote: Sammlungen löschen:
Fernbedienung: - my_collection_foo
Fernbedienung: - my_collection_bar
Remote: Neue Daten importieren... Fertig

In diesem Fall verwende ich sedFolgendes:

echo "$INSTALLCMD" | ssh -T "deploy@$SERVER" | sed -u "s/^/Remote: /"

Das Problem ist, wie ich bereits erklärte, dass keineteilweiseZeilen werden auf dem Bildschirm ausgegeben. Wenn ich das | sedTeil entferne, funktioniert es wie erwartet. Zunächst wird Folgendes geschrieben:

Neue Daten importieren...

Und wenige Sekunden später ist die Zeile vervollständigt:

Neue Daten importieren... Fertig

Ich gehe davon aus, seddass es nur zeilenweise arbeiten kann. Ich habe versucht, es auf ungepuffert einzustellen, aber es wartet immer noch auf ganze Zeilen. Gibt es eine andere Möglichkeit, dies zu erreichen?

Antwort1

Das ist etwas knifflig, weil all diese Dienstprogramme ( sed,,, ) zeilengepuffert sind. Das heißt, sie drucken die Ausgabe erst, wenn die Zeile beendet ist (ein Zeilenumbruch aufgetreten ist). Sie können die Eingabe nicht Zeichen für Zeichen lesen awk.grep

Deshalb habe ich zum Testen eine kleine Sequenz erstellt, die Ihr Verhalten simuliert:

{ 
  echo -n "first task: "
  sleep 2
  echo "done"
  echo -n "second task: "
  sleep 2
  echo "done"
}

Wie in Ihrer Frage wird first task:nach 2 Sekunden Folgendes gedruckt done. Probieren Sie es selbst aus, indem Sie es in Ihr Terminal kopieren.

Lösung:

Fügen Sie hinter Ihrem Befehl Folgendes hinzu:

IFS=
command | { x=1; while IFS= read -d'' -s -N 1 char; do
  [ $x ] && printf "Remote: "
  printf "$char"
  unset x
  [ "$char" == "
" ] && x=1
done; }

Erläuterung:

Das readeingebaute von bashkann Eingaben zeichenweise lesen. Der Teil read -d'' -s -N 1 chardeaktiviert das Trennzeichen -d'', aktiviert den stillen Modus -sund liest immer nur 1 Zeichen -N 1in die Variable ein $char. Dann prüft der Befehl, ob die Variable $xexistiert. Wenn ja, sind wir in einer neuen Zeile und drucken das „Präfix“. Dann drucken wir das Zeichen. Aufheben $x. Dann prüft die letzte Anweisung, ob das Zeichen ein Zeilenumbruch ist. Wenn es ein Zeilenumbruch ist, wird $xauf gesetzt 1und in der nächsten Schleife wird das „Präfix“ gedruckt.

Das Ganze kann getestet werden, indem man die beiden Sequenzen aneinanderreiht:

{ 
  echo -n "first task: "
  sleep 2
  echo "done"
  echo -n "second task: "
  sleep 2
  echo "done"
} | { x=1; while IFS= read -d'' -s -N 1 char; do
  [ $x ] && printf "Remote: "
  printf "$char"
  unset x
  [ "$char" == "
" ] && x=1
done; }

verwandte Informationen