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

14

Existe uma maneira de redirecionar a saída padrão de um processo em Win32 console para um canal nomeado ? Canais nomeados são embutidos no Windows e, embora sejam um conceito útil, nunca os vi usados na linha de comando.

Ou seja. como example.exe >\.\mypipe . (Esta sintaxe pode não estar correta, mas você entendeu.) Eu gostaria de poder redirecionar stdout e stderr para diferentes canais ao mesmo tempo.

Eu gostaria de evitar o uso de arquivos físicos como substitutos para escapar da lentidão do IO, buffers de IO, bloqueios de arquivos, direitos de acesso, espaço disponível no disco rígido, decisão de sobrescrever, persistência de desmembramentos, etc.

Outra razão é porque o Windows tradicional O conjunto de ferramentas não foi projetado em torno de uma filosofia baseada em arquivos (texto) tanto quanto como no Unix . Além disso, os pipes nomeados não poderiam ser facilmente montados no Windows, se necessário.

Por fim, há a curiosidade de que um bom conceito possa ser bem utilizado.

    
por n611x007 30.05.2012 / 11:33

3 respostas

6

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

pipes 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 é bem simples:

asdf.exe | NamedPipeServer.exe "APipeName"

Em outra janela do console:

NamedPipeClient.exe "APipeName"

Infelizmente, isso só pode redirecionar stdout (ou stdin ou combinado), não stderr por conta própria, devido a limitações no operador de canal ( | ) no prompt de comando do Windows. Se você descobrir como enviar stderr por meio desse operador de pipe, isso deve funcionar. Como alternativa, o servidor pode ser modificado para iniciar seu programa e redirecionar especificamente stderr . Se isso for necessário, me avise em um comentário (ou faça você mesmo); não é muito difícil se você tiver algum conhecimento de 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 será fechado 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 cano quebrado, principalmente porque não posso ser incomodado fazendo algo tão complicado agora . Também é limitado a um cliente por servidor .

Código-fonte

Estes são escritos em c #. Não há muito sentido em tentar explicá-lo. 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 ao arquivo
  3. Executar executável e redirecionar stderr output 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 tail funcionaria. O método PowerShell funciona de forma nativa no Windows, mas pode ser um pouco lento (por exemplo, há alguma latência entre a gravação no arquivo e a exibição na tela). Veja esta pergunta do StackOverflow para outros tail de alternativas.

O único problema é que o arquivo temporário pode ficar muito grande. Uma solução possível é executar um loop que só imprime se o arquivo tiver conteúdo e limpar o arquivo imediatamente depois, mas isso causaria uma condição de corrida.

    
por 30.05.2012 / 11:58
9

Estou surpreso que isso não tenha sido respondido corretamente. Na verdade, existe um caminho UNC atribuído a pipes nomeados pelo sistema, acessíveis em qualquer máquina na rede, que podem ser usados como um arquivo normal:

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

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

    
por 28.08.2017 / 20:25
1

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

    
por 30.05.2012 / 13:59