pipe – Windows how to redirect file parameter to stdout? (Windows equivalent of `/dev/stdout`) – Super User
Posted by jpluimers on 2022/02/02
TL;DR:
- Windows has
CON:
which is an equivalent for/dev/tty
- Windows has no equivalent for
/dev/stdout
(the standard output stream) - There is a C#
PipeServer.cs
proof-of-concept that allows to simulate/dev/stdout
through a temporary named pipe - Windows pipe names start with
\\.\pipe\
for names on the local machine - The above for
/dev/stdout
on Windows also holds for/dev/stdin
(the standard input stream)
All via [Wayback] pipe – Windows how to redirect file parameter to stdout? (Windows equivalent of /dev/stdout
) – Super User.
Windows CON
is like /dev/tty
Because of extensive backward compatibility to MS-DOS, Windows keeps these filenames as reserved and for most, including CON
has special functionality. The original DOS documentation of this has been taken off-line, but luckily there are archival sites: MS-DOS Device Driver Names Cannot be Used as File Names. That is the link to [Wayback] KB74496 before it was taken off-line. Originally, it was known as [Wayback] Q74496:
Microsoft MS-DOS reserves certain names for system device drivers. If you try to name a file using one of these names, you will receive the following error message:
Write Fault Error Writing Device <FILENAME.EXT>Abort, Retry, Ignore, Fail?
The solution is to change the file name.
…
Below is a list of default device driver names.
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
Basically MS-DOS CON
is like the Unix/Linux /dev/tty
Device file – Wikipedia, and Windows CON
is like the /dev/tty*
Virtual console – Wikipedia (since it is bound to the conhost.exe
Windows Console – Wikipedia)
Windows has no equivalent for /dev/stdout
On Linux, processes can read input from the special files /dev/stdin
or write output to /dev/stdout
, and you can specify those as file parameters. This is very useful when a tool by default does not read from /dev/stdin
or write to /dev/stdout
.
I wish Windows had an equivalent for /dev/stdout
(and /dev/stdin
). Since I could not find it, I asked [Wayback] pipe – Windows how to redirect file parameter to stdout?(Windows equivalent of /dev/stdout
) – Super User, which confirmed by suspicion (thanks [Wayback] grawity!):
Windows has no direct equivalent to
/dev/stdout
.
You can simulate /dev/stdout
and /dev/stdin
on Windows through a temporary named pipe provided through an intermediate program
[Wayback] grawity also wrote a proof-of-concept C# program that can provide access to standard input via a named pipe from a secondary console:
Run:
PipeServer foo in | programtwo
in window one, and:
programone \\.\pipe\foo
in window two.
I’ve copied the source to a gist having an initial PipeServer.cs
[Wayback].
Later, [Wayback] ntninja improved the proof-of-concept so a secondary console was not needed any more, which I copied to the second PipeServer.cs
[Wayback].
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}"
What happens underneath is that "{pipe}"
becomes a value like "\\.\pipe\pipeserver_1"
fully automatically (the tool tries from 1
up till 65535
).
Windows named pipes and named pipe names
Since named pipes are a relatively unknown feature for many people, a few important links (note they are not limited to Win32, it’s just that I could not find the generic documentation on them yet):
- Named pipe – Wikipedia
- [Wayback] Pipe Names – Win32 apps | Microsoft Docs
- [Wayback] Named Pipes – Win32 apps | Microsoft Docs
- [Wayback] [MS-WPO]: Named Pipes | Microsoft Docs
- [Wayback] Named Pipe Type, Read, and Wait Modes – Win32 apps | Microsoft Docs
- [Wayback] Pipelist – Windows Sysinternals | Microsoft Docs
Displays the named pipes on your system, including the number of maximum instances and active instances for each pipe.
…
Did you know that the device driver that implements named pipes is actually a file system driver? In fact, the driver’s name is
NPFS.SYS
, for “Named Pipe File System”. What you might also find surprising is that its possible to obtain a directory listing of the named pipes defined on a system. This fact is not documented, nor is it possible to do this using the Win32 API. Directly usingNtQueryDirectoryFile,
the native function that the Win32FindFile
APIs rely on, makes it possible to list the pipes. The directory listing NPFS returns also indicates the maximum number of pipe instances set for each pipe and the number of active instances.
–jeroen
Source code:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
Leave a Reply