Windows, ¿cómo redirigir el parámetro del archivo a la salida estándar? (Equivalente en Windows de `/dev/stdout`)

Windows, ¿cómo redirigir el parámetro del archivo a la salida estándar? (Equivalente en Windows de `/dev/stdout`)

Consola de Windows:

  • La herramienta A puede escribir datos binarios en un archivo, pero no tiene opción para indicarle que use la salida estándar.
  • La herramienta B puede leer datos binarios de la entrada estándar y procesar la información que contiene.

¿Cómo puedo obtener la salida de A canalizada a través de B sin utilizar un archivo intermedio?

En otras palabras: ¿cuál es el equivalente de Windows /dev/stdout?

Respuesta1

Windows tiene un análogo para /dev/stdout, CON:

Me imagino que todavía funciona, considerando el actual programa de "compatibilidad heredada" de Microsoft.

Ah... lo encontré. Soporte técnico de Microsoftda una lista de nombres reservados. No puede nombrar un archivo con estos nombres y tienen significados especiales cuando se usan como entradas o salidas.

PODRÍA poder utilizar CON como dispositivo de salida para enviar a la salida estándar.

La lista:

   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

Respuesta2

Windows no tiene un equivalente directo a /dev/stdout.


Aquí está mi intento de escribir un programa C# que cree untubería con nombre, que se puede asignar al programa A como nombre de archivo. Requiere .NET v4.

(C# porque el compilador viene con tiempo de ejecución .NET y ¿qué computadora no tiene .NET hoy en día?)

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);
        }
    }
}

Compilar con:

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

cscpuede encontrarse en%SystemRoot%\Microsoft.NET\Framework64\<version>\csc.exe

Por ejemplo, usando .NET Client Profile v4.0.30319 en un Windows XP de 32 bits:

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

Correr:

PipeServer foo in | programtwo

en la ventana uno, y:

programone \\.\pipe\foo

en la ventana dos.

Respuesta3

Residencia engravedadLa respuesta de He creado una versión extendida que permite iniciar un proceso directamente sin tener que usar múltiples ventanas de terminal.

Uso general:

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

Luego, la cadena "{pipe}" se reemplaza por la ruta de redirección.

Ejemplo del mundo real:

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

Esta línea de comandos se puede insertar directamente, por ejemplo, en Eclipse para redirigir el registro de compilación de un determinado constructor externo a StdOut.

Esto es probablemente lo mejor que hay...

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;
    }
}

información relacionada