No Windows, posso redirecionar o stdout para um pipe (nomeado) na linha de comando?

17

Existe uma maneira de redirecionar a saída padrão de um processo no console do Win32 para um pipe nomeado ? Os pipes nomeados são incorporados ao Windows e, embora sejam um conceito útil, nunca os vi usados ​​na linha de comando.

Ou seja. gosto example.exe >\\.\mypipe. (Essa sintaxe pode não estar correta, mas você entendeu.) Gostaria de poder redirecionar stdout e stderr para diferentes pipes ao mesmo tempo.

Eu gostaria de evitar o uso de arquivos físicos como substituto, para evitar lidar com lentidão de E / S, buffers de E / S, bloqueios de arquivo, direitos de acesso, espaço disponível no disco rígido, decisão de sobrescrever, persistência não sigilosa etc.

Outra razão é porque o conjunto tradicional de ferramentas do Windows não é projetado em torno de uma filosofia baseada em arquivo (texto) tanto quanto no Unix . Além disso, os pipes nomeados não podem ser facilmente montados no Windows, se houver.

Finalmente, há a curiosidade de saber se um bom conceito pode ser utilizado.

n611x007
fonte
11
Você quer dizer redirecionar para um pipe nomeado ou um slot de correio? Você tem / está disposto a escrever o fim do recebimento?
ixe013
Sim, como em um pipe ou slot de correio nomeado. Ainda não tenho, mas estou disposto a escrever o final do recebimento.
N611x007 6/06/12

Respostas:

8

Não sei por que você não deseja redirecionar para um arquivo. Existem dois métodos que fornecerei aqui. Um método é redirecionar e ler de um arquivo, o outro é um conjunto de programas.


Canais nomeados

O que fiz foi escrever dois programas para o .NET 4. Um envia a saída para um pipe nomeado, o outro lê esse pipe e é exibido no console. O uso é bastante simples:

asdf.exe | NamedPipeServer.exe "APipeName"

Em outra janela do console:

NamedPipeClient.exe "APipeName"

Infelizmente, isso só pode redirecionar stdout(ou stdinou combinar), não stderrpor si só, devido a limitações no operador de pipe ( |) no prompt de comando do Windows. Se você descobrir como enviar stderratravés desse operador de tubo, ele deverá funcionar. Como alternativa, o servidor pode ser modificado para iniciar seu programa e redirecionar especificamente stderr. Se necessário, informe-me em um comentário (ou faça você mesmo); não é muito difícil se você tiver algum conhecimento da biblioteca "Process" em C # e .NET.

Você pode baixar o servidor e o cliente .

Se você fechar o servidor após a conexão, o cliente fechará imediatamente. Se você fechar o cliente após a conexão, o servidor será fechado assim que você tentar enviar algo através dele. Não é possível reconectar um tubo quebrado, principalmente porque não posso me incomodar em fazer algo tão complicado agora. Também é limitado a um cliente por servidor .

Código fonte

Estes são escritos em c #. Não faz muito sentido tentar explicar isso. Eles usam o .NET NamedPipeServerStream e NamedPipeClientStream .

O servidor:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Pipes;
using System.IO;

namespace NamedPipeServer
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args == null || args.Length == 0)
            {
                Console.Error.WriteLine("[NamedPipeServer]: Need pipe name.");
                return;
            }

            NamedPipeServerStream PipeServer = new NamedPipeServerStream(args[0], System.IO.Pipes.PipeDirection.Out);
            PipeServer.WaitForConnection();
            StreamWriter PipeWriter = new StreamWriter(PipeServer);
            PipeWriter.AutoFlush = true;

            string tempWrite;

            while ((tempWrite = Console.ReadLine()) != null)
            {
                try
                {
                    PipeWriter.WriteLine(tempWrite);
                }
                catch (IOException ex)
                {
                    if (ex.Message == "Pipe is broken.")
                    {
                        Console.Error.WriteLine("[NamedPipeServer]: NamedPipeClient was closed, exiting");
                        return;
                    }
                }
            }

            PipeWriter.Close();
            PipeServer.Close();
        }
    }
}

O cliente:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Pipes;
using System.IO;

namespace NamedPipeClient
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args == null || args.Length == 0)
            {
                Console.Error.WriteLine("[NamedPipeClient]: Need pipe name.");
                return;
            }

            NamedPipeClientStream PipeClient = new NamedPipeClientStream(".", args[0], System.IO.Pipes.PipeDirection.In);
            PipeClient.Connect();
            StreamReader PipeReader = new StreamReader(PipeClient);

            string tempRead;

            while ((tempRead = PipeReader.ReadLine()) != null)
            {
                Console.WriteLine(tempRead);
            }

            PipeReader.Close();
            PipeClient.Close();
        }
    }
}

Redirecionando para um arquivo

type NUL>StdErr.temp
start powershell -c Get-Content StdErr.temp -Wait
MyExecutable.exe 2>StdErr.temp
  1. Crie um arquivo vazio
  2. Iniciar uma nova janela do console que assiste o arquivo
  3. Executar executável e redirecionar a stderrsaída para esse arquivo

Isso fornece o efeito desejado de uma janela do console para assistir stdout(e fornecer stdin) e outra para assistir stderr.

Qualquer coisa que imite tailfuncionaria. O método PowerShell funciona nativamente no Windows, mas pode ser um pouco lento (ou seja, há alguma latência entre a gravação no arquivo e a exibição na tela). Veja esta questão StackOverflow para outras tailalternativas.

O único problema é que o arquivo temporário pode crescer bastante. Uma solução possível é executar um loop que é impresso apenas se o arquivo tiver conteúdo e limpá-lo imediatamente depois, mas isso causaria uma condição de corrida.

Prumo
fonte
2
Os usuários do UNIX são os que mais se incomodam porque o Windows falha mais uma vez em implementar uma ideia de 40 anos de uma maneira sensata. Você não precisa escrever um programa personalizado toda vez que quiser fazer algo básico. facepalm
bambams
Veja abaixo: você pode usar o caminho UNC atribuído a um pipe nomeado e acessá-lo diretamente.
Erik Aronesty
15

Estou surpreso que isso ainda não tenha sido respondido corretamente. De fato, existe um caminho UNC atribuído aos pipes nomeados pelo sistema, acessível em qualquer máquina da rede, que pode ser usado como um arquivo normal:

program.exe >\\.\pipe\StdOutPipe 2>\\.\pipe\StdErrPipe

Supondo que os tubos denominados "StdOutPipe" e "StdErrPipe" existam nesta máquina, isso tenta se conectar e gravar neles. A pipeparte é o que especifica que você deseja um pipe nomeado.

IllidanS4 quer Monica de volta
fonte
Eu acho que isso deve ser marcado como a resposta correta, pois é exatamente o que está pedindo @ n611x007, não são necessários programas externos para fazer isso!
arturn
o problema é que você ainda precisa iniciar um serviço de algum tipo que crie esses pipes .... eles não existem independentemente de um programa que foi criado e destruído quando o programa desaparece.
Erik Aronesty
@ErikAronesty Presumi que esses tubos já existissem. Caso contrário, não há como criá-los apenas com o cmd.exe.
IllidanS4 quer Monica de volta
Sim, é a coisa legal sobre pipes Unix, você pode criar tubos da linha de comando
Erik Aronesty
1

Não com o shell padrão (CMD.EXE). Para programadores, é bastante fácil . Basta pegar os dois tubos de um processo que você iniciou.

MSalters
fonte
11
Somente prb é que a amostra usa pipes anônimos, que não suportam io sobreposto (assíncrono) e, portanto, são propensos a conflitos, ou pelo menos PeekNamedPipe deve ser usado.
Fernando Gonzalez Sanchez
11
As esperas de bloqueio não são conflitos, e o problema fundamental (o thread que consome dados impede o thread do produtor de produzi-lo) não é resolvido pela E / S sobreposta.
MSalters
Eu quis dizer isso: blogs.msdn.com/b/oldnewthing/archive/2011/07/07/10183884.aspx , um exemplo de impasse.
Fernando Gonzalez Sanchez
11
@FernandoGonzalezSanchez: Praticamente o mesmo problema. Observe que a solução recomendada (encadeamento extra) evita qualquer necessidade de E / S assíncrona.
MSalters
11
Sim, o prb no exemplo msdn é que o pai está preso para sempre, na função ReadFromPipe, linha bSuccess = ReadFile (g_hChildStd_OUT_Rd, chBuf, BUFSIZE & dwRead, NULL); lerá 70 bytes na primeira vez e a segunda vez ficará para sempre (PeekNamedPipe, que está ausente, não está bloqueando).
Fernando Gonzalez Sanchez
-1

Suas preferências para um canal de dados do Windows de um servidor para uma janela do cliente imediatamente ou mais tarde podem ser satisfeitas com uma pequena unidade de RAM. A mesma memória é alocada para os dados, gravados / lidos com um nome semelhante ao sistema de arquivos. O cliente exclui o arquivo usado e aguarda outro ou o deixa desaparecer quando o computador é desligado.

user999850
fonte