Kommandozeilenbefehle ausführen und die dazugehörige Ausgabe verarbeiten

  • VB.NET

Es gibt 17 Antworten in diesem Thema. Der letzte Beitrag () ist von Fakiz.

    Kommandozeilenbefehle ausführen und die dazugehörige Ausgabe verarbeiten

    Guten Morgen,

    schon seit einigen Tagen suche ich nach einer Möglichkeit, Kommandozeilenbefehle auszuführen und die dazugehörige Ausgabe zu verarbeiten.
    Ich habe es schon hinbekommen, Kommandozeilenbefehle auszuführen und mir die Ausgabe asynchron (synchron geht bei mir nicht, da z.B. ReadToEnd oder ReadLine meine Anwendung einfrieren) über die Ereignisse OutputDataReceived und ErrorDataReceived zurückgeben zu lassen.
    Das für mich momentan unlösbare Problem ist, dass ich keine Indikatoren dafür finde, ob der Kommandozeilenbefehl nun vollständig ausgeführt wurde UND ob eine Eingabe benötigt wird/wieder möglich ist.
    Über die Kommandozeile werden Git-, in Verbindung zu einem Web-Server auch Linux- und diverse andere Befehle ausgeführt.

    Ich wäre für Eure Hilfe sehr dankbar!
    "Denken ist die schwerste Arbeit, die es gibt. Das ist wahrscheinlich auch der Grund, warum sich so wenig Leute damit beschäftigen." - Henry Ford

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Vultrax“ ()

    @Vultrax
    Für jeden Befehl startest Du einen separaten Thread, in dem der abgearbeitet wird. Gib ihm eine ID (z.B. einen laufenden Integer-Wert).
    In einer List(Of Tnteger) oder einem Dictionary(Of Integer, String) merkst Du Dir die laufenden Vorgänge (ID und einen Namen dazu).
    Wenn ein Befehl abgearbeitet wurde, sendet er ein Event an das Hauptprogramm, in dem er seine ID übermittelt.
    Das Hauptprogramm kann dann diese ID aus der Liste löschen.
    Wenn Du die laufenden Threads in einer ListBox anzeigst, hast Du stets einen Überblick, was da los ist.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @RodFromGermany - Danke Dir erstmal für Deine schnelle Antwort.

    Die Kommandozeilenbefehle dürfen nicht gleichzeitig, sondern müssen der Reihe nach ausgeführt werden. In meiner Vorstellung habe ich eine Funktion, die einen Befehl ausführt und mir die Ausgabe als String zurückgibt.
    Ich verstehe in Deinem Beitrag nicht, in welcher Art und Weise ich feststellen kann, dass der Kommandozeilenbefehl ausgeführt wurde. Die Auslagerung in Threads habe ich mit "ReadToEnd" oder "ReadLine" probiert, allerdings friert der Thread ein und mehr kann ich nicht machen. Oder meinst du, dass ich immer eine neue Instanz von CMD erstellen soll?

    Mein aktueller Test-Code sieht so aus:

    VB.NET-Quellcode

    1. WithEvents CMD As Process = Process.Start(New ProcessStartInfo With {
    2. .FileName = "cmd.exe",
    3. .CreateNoWindow = False,
    4. .UseShellExecute = False,
    5. .RedirectStandardInput = True,
    6. .RedirectStandardOutput = True,
    7. .RedirectStandardError = True})
    8. Sub Main()
    9. CMD.BeginOutputReadLine()
    10. CMD.BeginErrorReadLine()
    11. CMD.StandardInput.WriteLine("ipconfig")
    12. Console.ReadKey()
    13. End Sub
    14. Private Sub CMD_OutputDataReceived(sender As Object, e As DataReceivedEventArgs) Handles CMD.OutputDataReceived
    15. Console.WriteLine(e.Data)
    16. End Sub
    17. Private Sub CMD_ErrorDataReceived(sender As Object, e As DataReceivedEventArgs) Handles CMD.ErrorDataReceived
    18. Console.WriteLine(e.Data)
    19. End Sub

    Die Ausgabe in der Konsole sieht dann wie folgt aus:

    "Denken ist die schwerste Arbeit, die es gibt. Das ist wahrscheinlich auch der Grund, warum sich so wenig Leute damit beschäftigen." - Henry Ford

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Vultrax“ ()

    Vultrax schrieb:

    in welcher Art und Weise ich feststellen kann, dass der Kommandozeilenbefehl ausgeführt wurde
    Wenn die ID gemeldet wird, ist der kommunizierende Thread beendet und somit die Aufgabe erfüllt.
    In diesem Moment kann der nächste Thread starten.
    Wenn Du die Threads mit der betreffenden ID in eine List(Of ID, Thread) packst, kannst Du unmittelbar den nächsten starten.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @RodFromGermany

    Möglicherweise verstehe ich das falsch, aber der Thread wird ja nicht beendet, weil er nicht mehr reagiert.
    Die Zeile "Console.WriteLine("Fertig")" wird niemals erreicht, weil er mir zwar die Ausgabe gibt, danach aber einfriert. In diesem Moment weiß ich dann aber trotzdem nicht, ob ich jede Ausgabe bekommen habe, weil der Kommandozeilenbefehl ja eventuell noch am arbeiten sein kann und Ausgaben zu einem späteren Zeitpunkt erfolgen. In diesem Beispiel ist er aber schon durch, bleibt aber dennoch eingefroren.
    ("CMD.BeginOutputReadLine()" und "CMD.BeginErrorReadLine()" müssen vorher entfernt werden)

    VB.NET-Quellcode

    1. Dim command As New Thread(
    2. Sub()
    3. CMD.StandardInput.WriteLine("ipconfig")
    4. Using streamReader As New StreamReader(CMD.StandardOutput.BaseStream)
    5. Do Until streamReader.EndOfStream
    6. Console.WriteLine(streamReader.ReadLine())
    7. Loop
    8. Console.WriteLine("Fertig")
    9. End Using
    10. End Sub)
    11. command.Start()
    12. Console.ReadKey()
    "Denken ist die schwerste Arbeit, die es gibt. Das ist wahrscheinlich auch der Grund, warum sich so wenig Leute damit beschäftigen." - Henry Ford

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Vultrax“ ()

    @Vultrax Moving Target.
    Schreib eine vollständige und belastbare Aufgabenstellung / Problembeschreibung.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @RodFromGermany

    Meine Anwendung führt mehrere Kommandozeilenbefehle in einer bestimmten Reihenfolge aus und soll die Ausgabe in meiner Konsole anzeigen. Es soll mir nun möglich sein zu unterscheiden, wenn eine Eingabe innerhalb der Ausführung notwendig ist (z.B. man installiert einen MySQL-Server via. Kommandozeilenbefehl und muss Konfigurationen in der Installationen einstellen) und wenn die Ausführung beendet wurde, damit der nächste Kommandozeilenbefehl ausgeführt werden kann. Das Problem ist, dass ich nicht weiß wie ich dies identifizieren kann und die Umsetzung über den synchronen Weg wie oben beschrieben meine Anwendung einfriert.
    "Denken ist die schwerste Arbeit, die es gibt. Das ist wahrscheinlich auch der Grund, warum sich so wenig Leute damit beschäftigen." - Henry Ford

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Vultrax“ ()

    @Vultrax Soll das ganze volllautomatisch ablaufen?
    Vielleicht gibt es die Möglichkeit, solche notwendigen Eingaben als Script beizufügen?
    Ansonsten weißt Du doch, welche Befehle mit einer weiteren Eingabe verknüpft sind.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @RodFromGermany

    Ja, das muss alles automatisch passieren. Die Eingaben und die Anzahl davon sind leider nicht immer gleich und es kann durchaus sein, dass ein Kommandozeilenbefehl aufgrund bestimmter Umstände nicht ausgeführt werden soll, wodurch dann die vorherige Eingabe unnötig und umständlich ist. Es wäre ja schon mal ein Schritt, wenn ich überhaupt identifizieren könnte, wann eine Eingabe (wieder) möglich ist.

    Im Moment erhalte ich die Ausgabe wie oben beschrieben über den asynchronen Vorgang mit CMD.BeginOutputReadLine() und CMD.BeginErrorReadLine(), nur liefert mir das keinerlei Erkenntnis dazu, ob die Ausführung abgeschlossen oder eine Eingabe erforderlich ist. Zu überprüfen, ob eine bestimmte Zeile in der Ausgabe auftaucht, die man als "Eingabe-Ausgabe" interpretieren kann, ist zu inkonsistent und unsicher für mein Vorhaben.

    Mein zweiter Versuch es über einen synchronen Vorgang zu testen endet darin, dass ich eine Ausgabe erhalte und dann meine Anwendung oder der dafür initialisierte Thread einfriert. Nun bin ich hier genauso (wenig) schlauer wie ich es aktuell schon bin. Ich benötige irgendwas, was mir die benötigten Informationen gibt. Es hat sich mir auch schon die Frage gestellt, ob es noch einen anderen Weg gibt, außer einen CMD-Prozess dafür zu verwenden, was mir generell sowieso schon etwas unprofessionell erscheint, aber eine andere Lösung scheint's wohl nicht zu geben wie mir Google mitteilt.

    Der Ablauf soll quasi so sein:

    1). Kommandozeilenbefehl wird ausgeführt
    2). Eingabe erforderlich -> Benutzer wird darüber benachrichtig und gibt diese ein -> führt die Ausführung fort
    3). Eingabe wieder erforderlich -> Benutzer wird darüber benachrichtig und gibt diese ein -> führt die Ausführung fort
    usw.
    4). Der Kommandozeilenbefehl wurde vollständig ausgeführt -> Benutzer wird darüber benachrichtigt -> Nächster Kommandozeilenbefehl wird ausgeführt -> (Wiederholung)
    "Denken ist die schwerste Arbeit, die es gibt. Das ist wahrscheinlich auch der Grund, warum sich so wenig Leute damit beschäftigen." - Henry Ford

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Vultrax“ ()

    @Vultrax
    Probier doch mal so was in der Art:

    VB.NET-Quellcode

    1. Public Class Form7
    2. Private Class Cmd
    3. Public Event Notification(sender As Object, e As EventArgs)
    4. Public Event Finshed(sender As Object, e As EventArgs)
    5. Property Command As String
    6. Property Param As String
    7. Public Sub Execute()
    8. Dim exiter As Boolean = False
    9. While Not exiter
    10. If MessageBox.Show("Eingabe", Me.Command, MessageBoxButtons.OKCancel) = vbOK Then
    11. exiter = True
    12. Else
    13. RaiseEvent Notification(Me, New EventArgs)
    14. End If
    15. End While
    16. RaiseEvent Finshed(Me, New EventArgs)
    17. End Sub
    18. End Class
    19. Private WithEvents myCmd As Cmd
    20. Private myCmdQueue As New Queue(Of Cmd)
    21. Private Sub Form7_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    22. For i As Integer = 0 To 3
    23. Dim cmd As New Cmd
    24. cmd.Command = "Command" & i.ToString
    25. cmd.Param = i.ToString
    26. myCmdQueue.Enqueue(cmd)
    27. Next
    28. End Sub
    29. Private Sub StartProgress(cmd As Cmd)
    30. AddHandler cmd.Notification, AddressOf CmdNotification
    31. AddHandler cmd.Finshed, AddressOf CmdFinshed
    32. cmd.Execute()
    33. End Sub
    34. Private Sub CmdNotification(sender As Object, e As EventArgs)
    35. MessageBox.Show("Notification")
    36. End Sub
    37. Private Sub CmdFinshed(sender As Object, e As EventArgs)
    38. Dim cmd As Cmd = DirectCast(sender, Cmd)
    39. RemoveHandler cmd.Notification, AddressOf CmdNotification
    40. RemoveHandler cmd.Finshed, AddressOf CmdFinshed
    41. If myCmdQueue.Count > 0 Then
    42. cmd = myCmdQueue.Dequeue
    43. AddHandler cmd.Notification, AddressOf CmdNotification
    44. AddHandler cmd.Finshed, AddressOf CmdFinshed
    45. cmd.Execute()
    46. End If
    47. End Sub
    48. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    49. StartProgress(myCmdQueue.Dequeue)
    50. End Sub
    51. End Class
    @Naifu

    Danke Dir für Deine Antwort, aber das ist leider keine Lösung für mein Problem. Das ganze Drumherum ist nicht die Sache, sondern nur die Identifizierung der genannten Dinge.
    "Denken ist die schwerste Arbeit, die es gibt. Das ist wahrscheinlich auch der Grund, warum sich so wenig Leute damit beschäftigen." - Henry Ford

    Vultrax schrieb:

    Über die Kommandozeile werden Git-, in Verbindung zu einem Web-Server auch Linux- und diverse andere Befehle ausgeführt.

    Ich muss darüber aber keine Anwendungen ausführen, sondern diverse Befehle, wie in meinem Startbeitrag angegeben.
    "Denken ist die schwerste Arbeit, die es gibt. Das ist wahrscheinlich auch der Grund, warum sich so wenig Leute damit beschäftigen." - Henry Ford
    @Fakiz

    In diesem Fall wäre sowas möglich, das weiß ich. Es war aber auch nur ein Beispiel.
    Wenn ich mehrere Kommandozeilenbefehle ausführen muss, kann ich doch nicht jedes Mal einen neuen Prozess (wenn es um so einen Fall wie in meinem Beispiel geht) starten ... wie super unprofessionell ist das denn. Wenn die Anwendung nicht beendet werden darf oder sie schlichtweg zu lange Startzeiten hat, hört's aber sowieso schon wieder auf. Das selbe mit einer SSH-Verbindung zu einem Server ... soll ich die jedes Mal wieder killen, nur um einen weiteren Befehl ausführen zu können? Selbst wenn ich die oben genannten Probleme gelöst hätte, wäre diese Lösung schlichtweg zu unsicher oder gar für manche Szenarien komplett unbrauchbar.

    Ich habe auch nochmal geschaut, ob ich es nicht vielleicht doch irgendwie mit einer Überprüfung der Ausgabe machen könnte und habe festgestellt, dass ich nicht die vollständige Ausgabe erhalte, weil das Ende fehlt ... Damit ist das wohl auch für die Tonne.
    "Denken ist die schwerste Arbeit, die es gibt. Das ist wahrscheinlich auch der Grund, warum sich so wenig Leute damit beschäftigen." - Henry Ford

    Vultrax schrieb:

    Wenn ich mehrere Kommandozeilenbefehle ausführen muss, kann ich doch nicht jedes Mal einen neuen Prozess (wenn es um so einen Fall wie in meinem Beispiel geht) starten ... wie super unprofessionell ist das denn.

    Bin mir nicht sicher ob du da nicht einem Irrtum unterliegst.
    Wenn ich mehrere Kommandozeilenbefehle ausführen muss, kann ich doch nicht jedes Mal einen neuen Prozess (wenn es um so einen Fall wie in meinem Beispiel geht) starten ... wie super unprofessionell ist das denn

    Wer sagt das denn? Consolen Befehle lassen sich unär und logisch verknüpfen.


    Das selbe mit einer SSH-Verbindung zu einem Server ... soll ich die jedes Mal wieder killen, nur um einen weiteren Befehl ausführen zu können?

    Du solltest dir dringend die Process Klasse ansehen du kannst auch den StdInput umleiten. Ich hab vor einigen Jahren mal was ähnliches gemacht damals wurden die Befehle zwar über eine TextBox eingegeben aber sollte das Prinzip veranschaulichen.
    Prozess In & Output umleiten

    dass ich nicht die vollständige Ausgabe erhalte

    Dann machst du wohl etwas falsch. Ohne zu wissen was du tust kann man nur Spekulieren.