In Windows, posso reindirizzare stdout a una pipe (denominata) nella riga di comando?


17

C'è un modo per reindirizzare l' output standard di un processo nella console Win32 su una pipe denominata ? Le pipe con nome sono integrate in Windows e sebbene possano essere un concetto utile, non le ho mai viste usate dalla riga di comando.

Vale a dire. come example.exe >\\.\mypipe. (Questa sintassi potrebbe non essere corretta ma si ottiene il punto.) Vorrei essere in grado di reindirizzare stdout e stderr su diverse pipe contemporaneamente.

Vorrei evitare di utilizzare i file fisici come sostituto, per evitare di occuparmi di lentezza dell'IO, buffer di I / O, blocco dei file, diritti di accesso, spazio disponibile su disco rigido, decisione di sovrascrivere, persistenza non modificata, ecc.

Un altro motivo è perché il tradizionale set di strumenti di Windows non è progettato attorno a una filosofia basata su file (di testo) come in Unix . Inoltre, le pipe con nome non potevano essere facilmente montate su Windows, se non del tutto.

Infine, c'è la curiosità di poter mettere a frutto un buon concetto.


1
Vuoi dire il reindirizzamento a una pipe denominata o a un mailslot? Hai / sei disposto a scrivere la parte ricevente?
ixe013,

Sì, ad esempio una pipa denominata o un mailslot. Non ho ancora, ma disposto a scrivere l'estremità ricevente.
n611x007,

Risposte:


8

Non sono sicuro del motivo per cui non si desidera reindirizzare a un file. Ci sono due metodi che fornirò qui. Un metodo consiste nel reindirizzare e leggere da un file, l'altro è un insieme di programmi.


Pipe nominate

Quello che ho fatto è stato scrivere due programmi per .NET 4. Uno invia l'output a una pipe denominata, l'altro legge da questa pipe e viene visualizzato sulla console. L'utilizzo è abbastanza semplice:

asdf.exe | NamedPipeServer.exe "APipeName"

In un'altra finestra della console:

NamedPipeClient.exe "APipeName"

Sfortunatamente, questo può solo reindirizzare stdout(o stdin, o combinato), non stderrda solo, a causa delle limitazioni nell'operatore pipe ( |) nel Prompt dei comandi di Windows. Se capisci come inviare stderrattraverso quell'operatore di pipa, dovrebbe funzionare. In alternativa, è possibile modificare il server per avviare il programma e reindirizzare in modo specifico stderr. Se è necessario, fammi sapere in un commento (o fallo da solo); non è troppo difficile se si ha una certa conoscenza della libreria "Process" di C # e .NET.

È possibile scaricare il server e il client .

Se si chiude il server dopo la connessione, il client si chiuderà immediatamente. Se si chiude il client dopo la connessione, il server si chiuderà non appena si tenta di inviare qualcosa attraverso di esso. Non è possibile ricollegare un tubo rotto, soprattutto perché non posso essere disturbato a fare qualcosa di così complicato in questo momento. È inoltre limitato a un client per server .

Codice sorgente

Questi sono scritti in C #. Non ha molto senso cercare di spiegarlo. Usano .NET NamedPipeServerStream e NamedPipeClientStream .

Il server:

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();
        }
    }
}

Il 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();
        }
    }
}

Reindirizzamento a un file

type NUL>StdErr.temp
start powershell -c Get-Content StdErr.temp -Wait
MyExecutable.exe 2>StdErr.temp
  1. Crea un file vuoto
  2. Avvia una nuova finestra della console che controlla il file
  3. Esegui eseguibile e reindirizza l' stderroutput su quel file

Ciò fornisce l'effetto desiderato di una finestra della console da guardare stdout(e fornire stdin) e un'altra da guardare stderr.

Tutto ciò che imita tailavrebbe funzionato. Il metodo PowerShell funziona in modo nativo in Windows, ma potrebbe essere un po 'lento (ovvero c'è una certa latenza tra la scrittura sul file e la visualizzazione sullo schermo). Vedi questa domanda StackOverflow per altre tailalternative.

L'unico problema è che il file temporaneo potrebbe diventare piuttosto grande. Una possibile soluzione alternativa consiste nell'eseguire un ciclo che stampa solo se il file ha contenuto e cancella il file immediatamente dopo, ma ciò causerebbe una condizione di competizione.


2
Gli utenti UNIX sono quelli che sono infastiditi perché Windows non riesce ancora a implementare un'idea di 40 anni in modo ragionevole. Non dovresti aver bisogno di scrivere un programma personalizzato ogni volta che vuoi fare una cosa di base. facepalm
bambams

Vedere di seguito: è possibile utilizzare il percorso UNC assegnato a una pipe denominata e accedervi direttamente.
Erik Aronesty,

15

Sono sorpreso che non sia già stata data una risposta corretta. Esiste davvero un percorso UNC assegnato alle pipe nominate dal sistema, accessibile su qualsiasi macchina della rete, che può essere usato come un normale file:

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

Supponendo che su questa macchina esistano pipe denominate "StdOutPipe" e "StdErrPipe", questo tenta di connettersi e scrivere su di esse. La pipeparte è ciò che specifica che si desidera una pipa denominata.


Penso che questo dovrebbe essere contrassegnato come la risposta corretta in quanto è proprio quello che chiede @ n611x007 non sono richiesti programmi esterni per fare questo!
arturn,

il problema è che è ancora necessario avviare un servizio di qualche tipo che crea quelle pipe ... non esistono indipendentemente da un programma creato e vengono distrutti quando il programma scompare.
Erik Aronesty,

@ErikAronesty Ho pensato che esistessero già queste pipe. Altrimenti, non è possibile crearli solo con cmd.exe.
IllidanS4 vuole che Monica torni il

Sì, questa è la cosa bella delle pipe unix, puoi creare pipe dalla riga di comando
Erik Aronesty,

1

Non con la shell standard (CMD.EXE). Per i programmatori, è abbastanza facile . Prendi semplicemente i due tubi di un processo che hai iniziato.


1
L'unico prb è che l'esempio usa pipe anonime, che non supportano io (asincrono) sovrapposto e quindi sono inclini a deadlock, o almeno PeekNamedPipe deve essere usato.
Fernando Gonzalez Sanchez,

1
Il blocco delle attese non è deadlock e il problema fondamentale (il thread che consuma i dati impedisce al thread del produttore di produrlo) non è comunque risolto da I / O sovrapposti.
Salterio del


1
@FernandoGonzalezSanchez: praticamente lo stesso problema. Si noti che la soluzione consigliata (thread aggiuntivo) elimina qualsiasi necessità di I / O asincrono.
MSalters il

1
Sì, il prb nell'esempio msdn è che il genitore è bloccato per sempre in attesa, nella funzione ReadFromPipe, line bSuccess = ReadFile (g_hChildStd_OUT_Rd, chBuf, BUFSIZE, & dwRead, NULL); leggerà 70 byte la prima volta e la seconda volta rimarrà bloccata per sempre (PeekNamedPipe, che manca, non blocca).
Fernando Gonzalez Sanchez,

-1

Le tue preferenze per una pipe di dati Windows da un server a una finestra dos client o immediatamente o successivamente potrebbero essere soddisfatte con una piccola unità RAM. La stessa memoria viene allocata per i dati, scritta / letta con un nome simile a un file system. Il client elimina il file esaurito e ne attende un altro o lo lascia scomparire quando il computer si spegne.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.