Windows how to redirect file parameter to stdout? (Windows equivalent of `/dev/stdout`)

12,717

Solution 1

Windows does have an analogue for /dev/stdout, CON:

I imagine it still works, considering Microsoft's ongoing "legacy compatibility" program.

Ah.. found it. Microsoft Support gives a list of reserved names. You cannot name a file these names, and they have special meanings when used as inputs or outputs.

You MIGHT be able to use CON as an output device to send to stdout.

The list:

   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

Solution 2

Windows has no direct equivalent to /dev/stdout.


Here is my attempt at writing a C# program that creates a named pipe, which can be given to program A as a filename. Requires .NET v4.

(C# because the compiler comes with .NET runtime, and what computer doesn't have .NET these days?)

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

Compile with:

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

csc can be found in %SystemRoot%\Microsoft.NET\Framework64\<version>\csc.exe

For example, using .NET Client Profile v4.0.30319 on a 32-bit Windows XP:

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

Run:

PipeServer foo in | programtwo

in window one, and:

programone \\.\pipe\foo

in window two.

Solution 3

Based on grawity's answer I've created an extended version which allows a process to be started directly without having to use multiple terminal windows.

General usage:

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

The string "{pipe}" is then replaced by the redirection path.

Real-World example:

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

This command-line can be inserted directly into, for example, Eclipse to redirect the build log of a certain external builder to StdOut.

This is probably the best it gets...

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;
    }
}
Share:
12,717

Related videos on Youtube

Jeroen Wiert Pluimers
Author by

Jeroen Wiert Pluimers

Makes things work. Specialist in .NET, Win32, x64, C#, SQL, Visual Studio and Delphi. Knows how to strike a balance between old and brand new technology to make things work. DOS, mobile, big systems, you name it. Surviving rectal cancer. Married to a cancer survivor. As curator responsible for his brother that has an IQ &lt; 50 and autism. 40+ year member of world class marching band Adest Musica. Trained and performed a few half marathons including 2013 NY and 2014 Naples, FL. Twitter: jpluimers Blog: wiert.me github: jpluimers LinkedIn: jwpluimers Keybase.io: wiert StackExchange/StackOverflow: stackexchange.com/users/14295

Updated on September 17, 2022

Comments

  • Jeroen Wiert Pluimers
    Jeroen Wiert Pluimers over 1 year

    Windows console:

    • Tool A can write binary data to a file, but has no option for telling it to use stdout.
    • Tool B can read binary data from stdin and process the info in it.

    How can I get the output from A piped through B without using an intermediate file?

    In other words: what is the Windows equivalent of /dev/stdout?

  • Jeroen Wiert Pluimers
    Jeroen Wiert Pluimers about 13 years
    +1 Now that is a cool solution! I will try that soon and let you know. Now first some Zzzzzzz :-)
  • Jeroen Wiert Pluimers
    Jeroen Wiert Pluimers about 13 years
    One more thought before doing Zzzz: what about closing the pipe? I'm going to think about what the best signalling mechanism is for programone to tell pipe, that it is done with \\.\pipe\foo
  • user1686
    user1686 about 13 years
    @Jeroen: When programone is done outputting data, it will simply close the output file it was using. (From the client side, pipes work the same way as ordinary files.) When it does so, pipe.exe – more precisely, pipe.CopyTo(...) – will reach EOF and simply exit.
  • user1686
    user1686 about 13 years
    @Jeroen: Also, there's one thing I haven't figured out: when using the tool in the opposite (out) direction (copy stdin to pipe), it dies with "Broken pipe" error after the first 1 kB. Doesn't happen when copying a pipe to stdout (in), however, so it shouldn't affect your program. (As they say, patches welcome.)
  • zod
    zod about 11 years
    I don't understand this line: programone \\.\pipe\foo - what does it mean, and how should I run it?
  • setevoy
    setevoy about 9 years
    Thanks, it really works (even in Win 8.1). We run build with Unity, in batch mode: Unity.exe -batchmode -projectPath C:\***\Application -logFile -buildWebPlayer web -quit. Without argument (filename) to -logFile - it must print output to console, but it doesn't. After adding CON (i.e. - -logFile CON) - it does :-)
  • bbodenmiller
    bbodenmiller about 7 years
    @setevoy this actually works for you with Unity on Windows? Can you give more details? Just causes Unity crash for me on Windows 7 and 10 using Unity.exe -batchmode -quit -projectPath "%cd%" -logFile CON -buildWindows64Player ".\GameBuild\Generic.exe". Command works fine without log file but I get no build output.
  • setevoy
    setevoy about 7 years
    @bbodenmiller Sorry, but can't provide any more information - this project was finished almost 2 years ago :-)
  • 2manyprojects
    2manyprojects almost 7 years
    @bbodenmiller: Did you figure out how to accomplish this on windows 10?
  • bbodenmiller
    bbodenmiller almost 7 years
    @2manyprojects see github.com/bbodenmiller/ci-build/blob/patch-2/Scripts/…... essentially I write to temporary file and just tail that file then when done delete temporary file.
  • 2manyprojects
    2manyprojects almost 7 years
    @bbodenmiller ok, if I understand you that's what I've been doing. I open a second PS window and tail the file from the default location. Unity will overwrite it the next time it runs.
  • bbodenmiller
    bbodenmiller almost 7 years
    If you checkout my script you can see that you can give unity custom log location for each run so it doesn't overwrite.
  • Verox
    Verox over 6 years
    @bbodenmiller It seems outputting the logfile to stdout is broken at the moment. See: issuetracker.unity3d.com/issues/…
  • Scott - Слава Україні
    Scott - Слава Україні almost 6 years
    I feel like I’m missing something — or maybe you are.  In Unix, /dev/tty is not equivalent to /dev/stdout.  Windows’ CON is equivalent to /dev/tty, not /dev/stdout.  This is not an answer to the question.
  • Egor   Skriptunoff
    Egor Skriptunoff over 5 years
    The answer is wrong. CON device is display. Writing to CON could be neither redirected (as in program > file.txt) nor used in a pipe (to pass data to another program's stdin as in program | another_program). The output written to CON always goes to display. The correct answer is "Windows doesn't have equivalent of /dev/stdout"
  • Jeroen Wiert Pluimers
    Jeroen Wiert Pluimers almost 3 years
    @zod that's the Windows way of using pipe named foo, see docs.microsoft.com/en-us/windows/win32/ipc/pipe-names
  • Jeroen Wiert Pluimers
    Jeroen Wiert Pluimers almost 3 years
    @user1686 I've added your code to this gist: gist.github.com/jpluimers/c38271933bdea49b1902fa88d568ab10/…
  • Jeroen Wiert Pluimers
    Jeroen Wiert Pluimers almost 3 years
    Thanks. I've added your code as a revision to this gist: gist.github.com/jpluimers/c38271933bdea49b1902fa88d568ab10/…