Das Build-Tool erzeugt einen Daemon, der seine Standardausgabe nicht schließt. Wie kann ich verhindern, dass dies meine Shell-Pipeline blockiert?

Das Build-Tool erzeugt einen Daemon, der seine Standardausgabe nicht schließt. Wie kann ich verhindern, dass dies meine Shell-Pipeline blockiert?

Ich verwende ein herstellerspezifisches Build-Tool (Closed Source, Microsemi Designer, ein FPGA-Layout-Tool). Ich rufe es über ein Shell-Skript auf (stark vereinfacht):

...  # Setup

/opt/.../designer SCRIPT:my_script.tcl |tee build.log

...  # Postprocessing

Das Problem besteht darin, dass der Prozess auch nach designerBeendigung teeweiter ausgeführt wird und das Skript somit nicht fortgesetzt wird.

Soweit ich es verstehe, liegt das daran, dass designerein langlebiger Daemon erzeugt wird, windu_scmd50und dieser Daemon schließt seine Standardausgabe (die er von erbt designer) nicht. Daher teewartet er auf ein EOF an seiner Standardeingabe (verbunden mit der Pipe), das nie kommt.

Beweise, dass dies die Ursache des Problems ist:

  • pszeigt, dass der Tee-Prozess noch läuft, der Designer-Prozess jedoch nicht.
  • Wenn ich den windu_scmd50Prozess beende, teewird er sofort beendet und das Skript wird fortgesetzt (dies kann ich jedoch in der Produktion nicht tun).
  • designerWenn ich die Ausgabe von nicht an tee(oder an etwas anderes; das Gleiche passiert mit catin anstelle von ) weiterleite tee, bleibt das Skript nicht hängen.
  • Wenn ich es designervon Jenkins aus ausführe (ohne die Ausgabe in eine Datei umzuleiten), benachrichtigt mich Jenkins, dass der „Prozess durchgesickerte Dateideskriptoren“ mit einer Verknüpfung zu diese Hilfeseite(offenbar befasst sich Jenkins ausdrücklich mit dieser Situation).

Also:Wie kann ich sicherstellen, dass der teeProzess gleichzeitig (oder kurz danach) beendet wird designer?

Einige Ansätze, die ich in Betracht gezogen habe:

  • Ist es möglich, die Standardausgabe eines laufenden Prozesses umzuleiten?
  • Gibt es ein Tool, das ich anstelle einer Shell-Pipe verwenden kann, um die Beendigung des designerProzesses zu erkennen?

Einige Ansätze, die nicht möglich sind:

  • Ich kann die Ausgabe des designerProzesses nicht ignorieren; ich muss sie in eine Protokolldatei schreiben.
  • Ich kann den Prozess nicht beenden, windu_scmd50da er möglicherweise von anderen Prozessen auf dem Computer verwendet wird.
  • Die aufdie Jenkins-Hilfeseitescheinen nicht zuzutreffen, da sie auch die Ausgabe von stummschalten würden designer.
  • Ich kann die Toolinstallation nicht ändern, um die windu_scmd50 ausführbare Datei durch einen Wrapper zu ersetzen.

Noch einige Anmerkungen:

  • Das Protokollieren der Ausgabe des windu_scmd50Prozesses wäre nett, ist aber keine Voraussetzung. Ich muss jedoch die Ausgabe des Designerprozesses protokollieren.
  • In der Praxis gibt es einen weiteren Filter zwischen designerund tee, der Zeitstempel hinzufügt ( designer ... |ts -s |tee build.log)
  • Die Verwendung von bash-spezifischen Funktionen ist zulässig. Kompatibilität mit shist keine Voraussetzung.

Hier ist ein Minimalbeispiel, das das Problem demonstriert ( test.sh):

#!/bin/bash
echo "Start"
sleep 10 &    # Background process that does not close stdout
echo "End"

Der direkte Aufruf dieses Skripts ( ./test.sh) gibt „Start“ und „End“ aus und beendet es sofort. Das Weiterleiten der Ausgabe an einen anderen Prozess ( ./test.sh |cat) gibt „Start“ und „End“ sofort aus, hält dann aber 10 Sekunden inne, bevor es beendet wird.

Antwort1

Eine recht einfache Problemumgehung besteht darin, nach Beendigung des Prozesses eine Nachricht in den Stream einzufügen designer. Ein catvorhergehender -ähnlicher Filter teesollte beendet werden, wenn er auf die Nachricht trifft.

Es wird ungefähr so ​​aussehen:


{ /opt/.../designer SCRIPT:my_script.tcl; echo message; } \
| sed -n '/^message$/q;p' | tee build.log

Anmerkungen:

  • Wenn designer(oder irgendetwas, das seine Standardausgabe erbt) die Nachricht ausgibt, sedwird das Programm vorzeitig beendet. Wählen Sie ein sTr1ng_Unlik3ly t0-Be enCOUnTered_in vvhat designer PRinT5. Das ASCII-EOT-Zeichen (Oktal 004) kann eine gute Wahl sein:
    { …; printf '\004\n'; } | sed -n "/^$(printf '\004')\$/q;p" | …

  • Es gibt Grenzen für das, was sedman bewältigen kann. Siehediese Antwort übergrep, bei ist es ähnlich sed.

  • ^message$entspricht einer vollständigen Zeile, die messagenur enthält. Dies setzt voraus, dass die Ausgabe (falls vorhanden) des designerProzesses mit einem Zeilenumbruchzeichen endet (d. h. die letzte Zeile ist vollständig; sieheLinieGegenunvollständige Zeile); nur dann messagewird es in einer eigenen Zeile stehen. Wenn designermit Sicherheit eine unvollständige Zeile erzeugt wird, dann wollen Sie sed -n '/message$/{s/message$//;p;q};p'. Wenn es in beide Richtungen gehen kann, dann wollen Sie sed -n '/^message$/q;/message$/{s/message$//;p;q};p'. Beachten Sie, dass die unvollständige Zeile von designerzu einer vollständigen Zeile wird.

  • Nicht-stumme Nachkommen von designerkönnen stören:

    • Sie können dazu führen, messagedass eine Zeile in der Mitte erscheint (selbst wenn sie versuchen, vollständige Zeilen auszugeben). Für uns sedwird es so sein, als ob designereine unvollständige Zeile generiert wurde.
    • Wenn die von Ihnen gewählte Nachricht sehr lang ist, wird sie möglicherweise in der Pipe mit anderen Daten verschachtelt angezeigt (siehediese AntwortUndPIPE_BUFHier). In diesem Fall sedwird es von uns überhaupt nicht erkannt.

verwandte Informationen