Consola do Windows:
- A ferramenta A pode gravar dados binários em um arquivo, mas não tem opção para instruí-lo a usar stdout.
- A ferramenta B pode ler dados binários do stdin e processar as informações nele contidas.
Como posso obter a saída de A canalizada através de B sem usar um arquivo intermediário?
Em outras palavras: qual é o equivalente do Windows /dev/stdout
?
Responder1
O Windows possui um análogo para /dev/stdout, CON:
Imagino que ainda funcione, considerando o programa contínuo de “compatibilidade herdada” da Microsoft.
Ah.. encontrei. Suporte da Microsoftfornece uma lista de nomes reservados. Você não pode nomear um arquivo com esses nomes e eles têm significados especiais quando usados como entradas ou saídas.
Você PODE usar CON como um dispositivo de saída para enviar para stdout.
A 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
Responder2
O Windows não tem equivalente direto ao /dev/stdout
.
Aqui está minha tentativa de escrever um programa C# que cria umtubo nomeado, que pode ser fornecido ao programa A como nome de arquivo. Requer .NET v4.
(C# porque o compilador vem com tempo de execução .NET e qual computador não tem .NET atualmente?)
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);
}
}
}
Ajuntar com:
csc PipeServer.cs /r:System.Core.dll
csc
pode ser encontrado em%SystemRoot%\Microsoft.NET\Framework64\<version>\csc.exe
Por exemplo, usando o .NET Client Profile v4.0.30319 em um 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
na janela um e:
programone \\.\pipe\foo
na janela dois.
Responder3
Baseado emgravidaderesposta de Eu criei uma versão estendida que permite que um processo seja iniciado diretamente sem a necessidade de usar várias janelas de terminal.
Uso geral:
PipeServer [in|out] [process name] [argument 1] [argument 2] [...]
A string "{pipe}" é então substituída pelo caminho de redirecionamento.
Exemplo do mundo real:
PipeServer.exe in "C:\Keil\UV4\Uv4.exe" -b "C:\Project\Project.uvproj" -j0 -o "{pipe}"
Esta linha de comandos pode ser inserida diretamente, por exemplo, no Eclipse para redirecionar o log de construção de um determinado construtor externo para StdOut.
Este é provavelmente o melhor que existe...
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;
}
}