Wie leitet man Dateiparameter unter Windows auf stdout um? (Windows-Äquivalent zu `/dev/stdout`)

Wie leitet man Dateiparameter unter Windows auf stdout um? (Windows-Äquivalent zu `/dev/stdout`)

Windows-Konsole:

  • Tool A kann Binärdaten in eine Datei schreiben, verfügt aber nicht über die Möglichkeit, es anzuweisen, stdout zu verwenden.
  • Tool B kann Binärdaten von stdin lesen und die darin enthaltenen Informationen verarbeiten.

Wie kann ich die Ausgabe von A durch B weiterleiten, ohne eine Zwischendatei zu verwenden?

Mit anderen Worten: Was ist das Windows-Äquivalent von /dev/stdout?

Antwort1

Windows hat ein Analogon für /dev/stdout, CON:

Ich kann mir vorstellen, dass es angesichts des laufenden „Legacy-Kompatibilitätsprogramms“ von Microsoft immer noch funktioniert.

Ah... gefunden. Microsoft-Supportgibt eine Liste reservierter Namen an. Sie können einer Datei diese Namen nicht geben und sie haben eine besondere Bedeutung, wenn sie als Ein- oder Ausgabe verwendet werden.

Möglicherweise können Sie CON als Ausgabegerät zum Senden an stdout verwenden.

Die Liste:

   Name    Function
   ----    --------
   CON     Keyboard and display
   PRN     System list device, usually a parallel port
   AUX     Auxiliary device, usually a serial port
   CLOCK$  System real-time clock
   NUL     Bit-bucket device
   A:-Z:   Drive letters
   COM1    First serial communications port
   LPT1    First parallel printer port
   LPT2    Second parallel printer port
   LPT3    Third parallel printer port
   COM2    Second serial communications port
   COM3    Third serial communications port
   COM4    Fourth serial communications port

Antwort2

Windows hat kein direktes Äquivalent zu /dev/stdout.


Hier ist mein Versuch, ein C#-Programm zu schreiben, das einBenannte Pipe, der Programm A als Dateiname übergeben werden kann. Erfordert .NET v4.

(C#, weil der Compiler mit einer .NET-Runtime geliefert wird und welcher Computer verfügt heutzutage nicht über .NET?)

PipeServer.cs

using System;
using System.IO;
using System.IO.Pipes;

class PipeServer {
    static int Main(string[] args) {
        string usage = "Usage: PipeServer <name> <in | out>";
        if (args.Length != 2) {
            Console.WriteLine(usage);
            return 1;
        }
        
        string name = args[0];
        if (String.Compare(args[1], "in") == 0) {
            Pipe(name, PipeDirection.In);
        }
        else if (String.Compare(args[1], "out") == 0) {
            Pipe(name, PipeDirection.Out);
        }
        else {
            Console.WriteLine(usage);
            return 1;
        }
        return 0;
    }
    
    static void Pipe(string name, PipeDirection dir) {
        NamedPipeServerStream pipe = new NamedPipeServerStream(name, dir, 1);
        pipe.WaitForConnection();
        try {
            switch (dir) {
                case PipeDirection.In:
                    pipe.CopyTo(Console.OpenStandardOutput());
                    break;
                case PipeDirection.Out:
                    Console.OpenStandardInput().CopyTo(pipe);
                    break;
                default:
                    Console.WriteLine("unsupported direction {0}", dir);
                    return;
            }
        } catch (IOException e) {
            Console.WriteLine("error: {0}", e.Message);
        }
    }
}

Kompilieren mit:

csc PipeServer.cs /r:System.Core.dll

csckann gefunden werden in%SystemRoot%\Microsoft.NET\Framework64\<version>\csc.exe

Beispielsweise bei Verwendung von .NET Client Profile v4.0.30319 auf einem 32-Bit-Windows XP:

"C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\csc.exe" PipeServer.cs /r:System.Core.dll

Laufen:

PipeServer foo in | programtwo

in Fenster eins, und:

programone \\.\pipe\foo

in Fenster zwei.

Antwort3

Bezogen aufSchwerkraftAls Antwort auf diese Frage habe ich eine erweiterte Version erstellt, die es ermöglicht, einen Prozess direkt zu starten, ohne mehrere Terminalfenster verwenden zu müssen.

Allgemeine Verwendung:

PipeServer [in|out] [process name] [argument 1] [argument 2] [...]

Die Zeichenfolge „{pipe}“ wird dann durch den Umleitungspfad ersetzt.

Beispiel aus der Praxis:

PipeServer.exe in "C:\Keil\UV4\Uv4.exe" -b "C:\Project\Project.uvproj" -j0 -o "{pipe}"

Diese Befehlszeile kann beispielsweise direkt in Eclipse eingefügt werden, um das Build-Protokoll eines bestimmten externen Builders nach StdOut umzuleiten.

Das ist wahrscheinlich das Beste, was es gibt ...

class PipeServer
{
    static
    int
    Main(string[] args)
    {
        if(args.Length < 2
        ||(System.String.Compare(args[0], "in")  != 0
        && System.String.Compare(args[0], "out") != 0)) {
            System.Console.WriteLine("Usage: PipeServer <in | out> <process> <args>");
            
            return 1;
        }
 
        ///////////////////////////////////
        // // // Process arguments // // //
        ///////////////////////////////////
        // Convert pipe direction
        System.IO.Pipes.PipeDirection pipe_dir = 0;
        if(System.String.Compare(args[0], "in") == 0) {
            pipe_dir = System.IO.Pipes.PipeDirection.In;
        }
        if(System.String.Compare(args[0], "out") == 0) {
            pipe_dir = System.IO.Pipes.PipeDirection.Out;
        }
        
        // Find process name to start
        string proc_name = args[1];
        
        // Build commandline argument string
        string proc_args = "";
        for(System.UInt16 i = 2; i < args.Length; i++) {
            if(args[i].IndexOf(" ") > -1) {
                proc_args += "\"" + args[i].Replace("\"", "\\\"") + "\" ";
            } else {
                proc_args += args[i] + " ";
            }
        }
        
        // Create server
        string                                pipe_name   = "";
        System.IO.Pipes.NamedPipeServerStream pipe_stream = null;
        for(System.UInt16 i = 1; i < 65535; i++) {
            // Generate new pipe name
            pipe_name = "pipeserver_" + System.Convert.ToString(i);
            
            try {
                // Start server
                pipe_stream = new System.IO.Pipes.NamedPipeServerStream(pipe_name, pipe_dir, 1);
                
                break;
            } catch(System.IO.IOException _) {
                continue;
            }
        }
        if(pipe_stream == null) {
            System.Console.WriteLine("Could not create pipe");
            
            return 1;
        }
        
        // Make sure the process knows about the pipe name
        proc_args = proc_args.Replace("{pipe}", "\\\\.\\pipe\\" + pipe_name);
        
        // Run process
        System.Diagnostics.Process proc = new System.Diagnostics.Process();
        proc.StartInfo.FileName  = proc_name;
        proc.StartInfo.Arguments = proc_args;
        proc.Start();
        
        // Connect pipes and wait until EOF
        pipe_stream.WaitForConnection();
        try {
            if(pipe_dir == System.IO.Pipes.PipeDirection.In) {
                pipe_stream.CopyTo(System.Console.OpenStandardOutput());
            }
            if(pipe_dir == System.IO.Pipes.PipeDirection.Out) {
                System.Console.OpenStandardInput().CopyTo(pipe_stream);
            }
        } catch (System.IO.IOException e) {
            System.Console.WriteLine("error: {0}", e.Message);
            
            return 1;
        }
        
        // Wait for process termination
        while(!proc.HasExited) {
            proc.WaitForExit();
        }
        
        // Return correct exit code
        return proc.ExitCode;
    }
}

verwandte Informationen