Windows how to redirect file parameter to stdout? (Windows equivalent of `/dev/stdout`)
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
?
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;
}
}