Kann ich in Windows stdout an eine (benannte) Pipe in der Befehlszeile umleiten?

17

Gibt es eine Möglichkeit, die Standardausgabe eines Prozesses in der Win32-Konsole an eine Named Pipe umzuleiten ? Named Pipes sind in Windows integriert und obwohl sie ein nützliches Konzept wären, habe ich sie noch nie von der Befehlszeile aus gesehen.

Dh wie example.exe >\\.\mypipe. (Diese Syntax ist möglicherweise nicht korrekt, aber Sie verstehen den Punkt.) Ich möchte in der Lage sein, stdout und stderr gleichzeitig auf verschiedene Pipes umzuleiten .

Ich möchte vermeiden, physische Dateien als Ersatz zu verwenden, um nicht mit E / A-Langsamkeit, E / A-Puffern, Dateisperren, Zugriffsrechten, verfügbarem Festplattenspeicher, Überschreibungsentscheidungen, unbeabsichtigter Persistenz usw. umzugehen.

Ein weiterer Grund ist, dass die traditionellen Windows- Tools nicht so sehr auf einer (text-) dateibasierten Philosophie basieren wie unter Unix . Außerdem konnten Named Pipes in Windows nicht ohne weiteres bereitgestellt werden, wenn überhaupt.

Schließlich besteht die Neugier, ob ein gutes Konzept sinnvoll eingesetzt werden kann.

n611x007
quelle
1
Sie möchten auf eine Named Pipe oder einen Mail-Slot umleiten? Haben / wollen Sie das Empfangsende schreiben?
ixe013
Ja, wie bei einer Named Pipe oder einem Mail-Slot. Ich habe noch nicht, aber ich bin bereit, das Empfangsende zu schreiben.
n611x007

Antworten:

8

Ich bin mir nicht sicher, warum Sie nicht zu einer Datei umleiten möchten. Es gibt zwei Methoden, die ich hier zur Verfügung stellen werde. Eine Methode ist das Weiterleiten und Lesen einer Datei, die andere ist eine Reihe von Programmen.


Named Pipes

Ich habe zwei Programme für .NET 4 geschrieben. Eines sendet die Ausgabe an eine Named Pipe, das andere liest von dieser Pipe und zeigt sie an der Konsole an. Die Verwendung ist ganz einfach:

asdf.exe | NamedPipeServer.exe "APipeName"

In einem anderen Konsolenfenster:

NamedPipeClient.exe "APipeName"

Leider kann dies aufgrund von Einschränkungen des Pipe-Operators ( ) in der Windows-Eingabeaufforderung nur umleiten stdout(oder stdinkombinieren), nicht stderrvon selbst |. Wenn Sie herausfinden, wie Sie stderrdurch diesen Pipe-Operator senden , sollte es funktionieren. Alternativ könnte der Server geändert werden, um Ihr Programm zu starten und gezielt umzuleiten stderr. Wenn das nötig ist, lass es mich in einem Kommentar wissen (oder mach es selbst); Es ist nicht allzu schwierig, wenn Sie über C # - und .NET- "Process" -Bibliothekskenntnisse verfügen.

Sie können den Server und den Client herunterladen .

Wenn Sie den Server nach der Verbindung schließen, wird der Client sofort geschlossen. Wenn Sie den Client nach der Verbindung schließen, wird der Server geschlossen, sobald Sie versuchen, etwas durch ihn zu senden. Es ist nicht möglich, ein defektes Rohr wieder anzuschließen, vor allem, weil ich mich momentan nicht darum kümmere, etwas so Kompliziertes zu tun. Es ist auch auf einen Client pro Server beschränkt .

Quellcode

Diese sind in C # geschrieben. Es macht nicht viel Sinn, es zu erklären. Sie verwenden .NET NamedPipeServerStream und NamedPipeClientStream .

Der Kellner:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Pipes;
using System.IO;

namespace NamedPipeServer
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args == null || args.Length == 0)
            {
                Console.Error.WriteLine("[NamedPipeServer]: Need pipe name.");
                return;
            }

            NamedPipeServerStream PipeServer = new NamedPipeServerStream(args[0], System.IO.Pipes.PipeDirection.Out);
            PipeServer.WaitForConnection();
            StreamWriter PipeWriter = new StreamWriter(PipeServer);
            PipeWriter.AutoFlush = true;

            string tempWrite;

            while ((tempWrite = Console.ReadLine()) != null)
            {
                try
                {
                    PipeWriter.WriteLine(tempWrite);
                }
                catch (IOException ex)
                {
                    if (ex.Message == "Pipe is broken.")
                    {
                        Console.Error.WriteLine("[NamedPipeServer]: NamedPipeClient was closed, exiting");
                        return;
                    }
                }
            }

            PipeWriter.Close();
            PipeServer.Close();
        }
    }
}

Der Kunde:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Pipes;
using System.IO;

namespace NamedPipeClient
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args == null || args.Length == 0)
            {
                Console.Error.WriteLine("[NamedPipeClient]: Need pipe name.");
                return;
            }

            NamedPipeClientStream PipeClient = new NamedPipeClientStream(".", args[0], System.IO.Pipes.PipeDirection.In);
            PipeClient.Connect();
            StreamReader PipeReader = new StreamReader(PipeClient);

            string tempRead;

            while ((tempRead = PipeReader.ReadLine()) != null)
            {
                Console.WriteLine(tempRead);
            }

            PipeReader.Close();
            PipeClient.Close();
        }
    }
}

In eine Datei umleiten

type NUL>StdErr.temp
start powershell -c Get-Content StdErr.temp -Wait
MyExecutable.exe 2>StdErr.temp
  1. Erstellen Sie eine leere Datei
  2. Starten Sie ein neues Konsolenfenster, das die Datei überwacht
  3. Führen Sie die ausführbare Datei aus und leiten Sie die stderrAusgabe in diese Datei um

Dies bietet den gewünschten Effekt, wenn ein Konsolenfenster überwacht stdout(und bereitgestellt stdin) und ein anderes überwacht wird stderr.

Alles, was das nachahmt, tailwürde funktionieren. Die PowerShell-Methode funktioniert von Haus aus unter Windows, ist jedoch möglicherweise etwas langsam (dh zwischen dem Schreiben in die Datei und dem Anzeigen auf dem Bildschirm besteht eine gewisse Latenz). Weitere Alternativen finden Sie in dieser StackOverflow-Fragetail .

Das einzige Problem ist, dass die temporäre Datei sehr groß werden kann. Eine mögliche Problemumgehung besteht darin, eine Schleife auszuführen, die nur gedruckt wird, wenn die Datei Inhalt enthält, und die Datei anschließend sofort zu löschen, was jedoch zu einer Race-Bedingung führen würde.

Bob
quelle
2
UNIX-Benutzer sind es, die sich darüber ärgern, dass Windows eine 40 Jahre alte Idee nicht sinnvoll umsetzt. Sie sollten nicht jedes Mal ein benutzerdefiniertes Programm schreiben müssen, wenn Sie eine grundlegende Aufgabe ausführen möchten. facepalm
bambams
Siehe unten: Sie können den UNC-Pfad verwenden, der einer Named Pipe zugewiesen ist, und direkt darauf zugreifen.
Erik Aronesty
15

Ich bin überrascht, dass dies noch nicht richtig beantwortet wurde. Das System weist Named Pipes einen UNC-Pfad zu, auf den auf allen Computern im Netzwerk zugegriffen werden kann. Dieser Pfad kann wie eine normale Datei verwendet werden:

program.exe >\\.\pipe\StdOutPipe 2>\\.\pipe\StdErrPipe

Vorausgesetzt, auf diesem Computer sind Pipes mit den Namen "StdOutPipe" und "StdErrPipe" vorhanden, wird versucht, eine Verbindung herzustellen und darauf zu schreiben. Der pipeTeil gibt an, dass Sie eine Named Pipe wünschen.

IllidanS4 will Monica zurück
quelle
Ich denke, dies sollte als die richtige Antwort markiert werden, da nur das, was @ n611x007 fragt, keine externen Programme erforderlich sind, um dies zu tun!
Arturn
Das Problem ist, dass Sie immer noch einen Dienst starten müssen, der diese Pipes erstellt. Sie existieren nicht unabhängig von einem Programm, das erstellt wurde und zerstört wird, wenn das Programm nicht mehr verfügbar ist.
Erik Aronesty
@ErikAronesty Ich nahm an, dass diese Pipes bereits existieren. Andernfalls können sie nicht nur mit cmd.exe erstellt werden.
IllidanS4 will Monica
Ja, das ist das Coole an Unix-Pipes. Sie können Pipes über die Befehlszeile erstellen
Erik Aronesty,
1

Nicht mit der Standard-Shell (CMD.EXE). Für Programmierer ist es ziemlich einfach . Nehmen Sie einfach die beiden Pipes eines Prozesses, den Sie gestartet haben.

MSalters
quelle
1
Das einzige Problem ist, dass das Beispiel anonyme Pipes verwendet, die keine überlappenden io (async) unterstützen und daher für Deadlocks anfällig sind, oder zumindest PeekNamedPipe muss verwendet werden.
Fernando Gonzalez Sanchez
1
Das Blockieren von Wartezeiten ist kein Deadlock, und das grundlegende Problem (der Datenverbrauch blockiert den Produzententhread) wird ohnehin nicht durch überlappende E / A gelöst.
MSalters
Ich meinte das: blogs.msdn.com/b/oldnewthing/archive/2011/07/07/10183884.aspx , ein Deadlock-Beispiel.
Fernando Gonzalez Sanchez
1
@FernandoGonzalezSanchez: Ziemlich das gleiche Problem. Beachten Sie, dass die empfohlene Lösung (zusätzlicher Thread) die Notwendigkeit für asynchrone E / A umgeht.
MSalters
1
Ja, das prb im msdn-Beispiel ist, dass Eltern für immer warten müssen, in der Funktion ReadFromPipe, Zeile bSuccess = ReadFile (g_hChildStd_OUT_Rd, chBuf, BUFSIZE, & dwRead, NULL); liest das erste Mal 70 Bytes und das zweite Mal bleibt es für immer (die fehlende PeekNamedPipe blockiert nicht).
Fernando Gonzalez Sanchez
-1

Ihre Vorlieben für eine Windows-Pipe von Daten von einem Server zu einem Client-DOS-Fenster, entweder sofort oder später, könnten mit einem kleinen RAM-Laufwerk zufrieden sein. Derselbe Speicher wird für die Daten reserviert, die mit einem dateisystemähnlichen Namen geschrieben / gelesen werden. Der Client löscht entweder die verbrauchte Datei und wartet auf eine andere oder lässt sie beim Herunterfahren des Computers verschwinden.

user999850
quelle