Czy w systemie Windows mogę przekierować stdout do potoku (nazwanego) w wierszu poleceń?

16

Czy istnieje sposób przekierowania standardowego wyjścia procesu w konsoli Win32 na nazwany potok ? Nazwane potoki są wbudowane w Windows i chociaż byłyby użyteczną koncepcją, nigdy nie widziałem ich z linii poleceń.

To znaczy. jak example.exe >\\.\mypipe. (Ta składnia może nie być poprawna, ale masz rację.) Chciałbym móc przekierować stdout i stderr do różnych potoków w tym samym czasie.

Chciałbym uniknąć używania plików fizycznych jako substytutu, aby uniknąć radzenia sobie z powolnością IO, buforami IO, blokadami plików, prawami dostępu, dostępnym miejscem na dysku twardym, decyzją o nadpisaniu, niezmienną trwałością itp.

Innym powodem jest to, że tradycyjny zestaw narzędzi systemu Windows nie jest zaprojektowany w oparciu o filozofię (tekstową) opartą na plikach, tak samo jak w Uniksie . Również nazwanych rur nie można łatwo zamontować w systemie Windows, jeśli w ogóle.

Wreszcie istnieje ciekawość, czy dobry pomysł mógłby zostać dobrze wykorzystany.

n611x007
źródło
1
Masz na myśli przekierowanie do nazwanego potoku lub skrzynki pocztowej? Czy masz / jesteś gotów napisać odbiorcę?
ixe013
Tak, jak w nazwanym potoku lub skrzynce pocztowej. Nie mam jeszcze, ale chcę napisać odbiorcę.
n611x007

Odpowiedzi:

8

Nie jestem pewien, dlaczego nie chcesz przekierowywać do pliku. Istnieją dwie metody, które tutaj przedstawię. Jedną z metod jest przekierowanie do pliku i odczyt z niego, drugi to zestaw programów.


Nazwane rury

To, co zrobiłem, to napisanie dwóch programów dla .NET 4. Jeden wysyła wyjście do nazwanego potoku, drugi odczytuje z tego potoku i wyświetla na konsoli. Użycie jest bardzo proste:

asdf.exe | NamedPipeServer.exe "APipeName"

W innym oknie konsoli:

NamedPipeClient.exe "APipeName"

Niestety, może to tylko przekierować stdout(lub stdinpołączyć), a nie stderrsamo, z powodu ograniczeń w operatorze potoku ( |) w wierszu polecenia systemu Windows. Jeśli dowiesz się, jak wysłać stderrza pośrednictwem tego operatora, powinien działać. Alternatywnie serwer można zmodyfikować, aby uruchomić program, a konkretnie przekierować stderr. Jeśli to konieczne, daj mi znać w komentarzu (lub zrób to sam); nie jest to zbyt trudne, jeśli masz wiedzę o bibliotece „Proces” C # i .NET.

Możesz pobrać serwer i klienta .

Jeśli zamkniesz serwer po połączeniu, klient natychmiast się zamknie. Jeśli zamkniesz klienta po połączeniu, serwer zamknie się, gdy tylko spróbujesz coś przez niego wysłać. Ponowne podłączenie uszkodzonej rury nie jest możliwe, głównie dlatego, że nie mogę się teraz martwić robieniem czegoś tak skomplikowanego. Jest również ograniczona do jednego klienta na serwer .

Kod źródłowy

Są one napisane w C #. Nie ma sensu tego tłumaczyć. Korzystają z .NET NamedPipeServerStream i NamedPipeClientStream .

Serwer:

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

Klient:

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

Przekierowanie do pliku

type NUL>StdErr.temp
start powershell -c Get-Content StdErr.temp -Wait
MyExecutable.exe 2>StdErr.temp
  1. Utwórz pusty plik
  2. Uruchom nowe okno konsoli, które obserwuje plik
  3. Uruchom plik wykonywalny i przekieruj stderrdane wyjściowe do tego pliku

Zapewnia to pożądany efekt jednego okna konsoli do oglądania stdout(i udostępniania stdin), a drugiego do oglądania stderr.

Wszystko, co naśladuje tail, zadziałałoby. Metoda PowerShell działa natywnie w systemie Windows, ale może być nieco powolna (tzn. Istnieje pewne opóźnienie między zapisaniem pliku a wyświetleniem na ekranie). Zobacz to pytanie StackOverflow dla innych tailalternatyw.

Jedynym problemem jest to, że plik tymczasowy może być dość duży. Możliwym obejściem jest uruchomienie pętli, która drukuje tylko wtedy, gdy plik ma zawartość i natychmiast wyczyści plik, ale spowoduje to sytuację wyścigu.

Kok
źródło
2
Użytkownicy systemu UNIX są tymi, którzy są zirytowani, ponieważ system Windows po raz kolejny zawodzi, aby w rozsądny sposób wdrożyć 40-letni pomysł. Nie musisz pisać niestandardowego programu za każdym razem, gdy chcesz zrobić coś podstawowego. facepalm
bambams
Zobacz poniżej: możesz użyć ścieżki UNC przypisanej do potoku nazwanego i uzyskać do niego bezpośredni dostęp.
Erik Aronesty
13

Dziwię się, że odpowiedź na to pytanie już nie została poprawna. W systemie istnieje ścieżka UNC przypisana potokom nazwanym, dostępna na każdej maszynie w sieci, która może być używana jak zwykły plik:

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

Zakładając, że na tym komputerze istnieją rury o nazwie „StdOutPipe” i „StdErrPipe”, próbuje się połączyć i zapisać do nich. pipeCzęścią jest to, co określa, że chcesz nazwanego potoku.

IllidanS4
źródło
Myślę, że powinno to być oznaczone jako poprawna odpowiedź, ponieważ to, co pyta @ n611x007, nie wymaga zewnętrznych programów do tego celu!
arturn
problem polega na tym, że wciąż musisz uruchomić usługę, która tworzy takie potoki .... nie istnieją niezależnie od programu, który stworzył i są niszczone, gdy program zniknie.
Erik Aronesty
@ErikAronesty Zakładałem, że te rury już istnieją. W przeciwnym razie nie ma możliwości utworzenia ich tylko za pomocą cmd.exe.
IllidanS4
Tak, to fajna rzecz w
potokach
1

Nie ze standardową powłoką (CMD.EXE). Dla programistów jest to dość łatwe . Po prostu złap dwie rury procesu, który rozpocząłeś.

MSALTERY
źródło
1
Tylko prb jest taki, że przykład używa anonimowych potoków, które nie obsługują nakładających się io (async), a zatem są podatne na zakleszczenia lub przynajmniej PeekNamedPipe musi być użyte.
Fernando Gonzalez Sanchez
1
Blokowanie oczekiwania nie jest zakleszczeniem, a podstawowy problem (zajmujący dane wątek producenta bloków wątku z jego wytworzenia) nie jest i tak rozwiązywany przez nakładające się operacje we / wy.
MSalters
Miałem na myśli to: blogs.msdn.com/b/oldnewthing/archive/2011/07/07/10183884.aspx , przykład zakleszczenia.
Fernando Gonzalez Sanchez
1
@FernandoGonzalezSanchez: Prawie ten sam problem. Zauważ, że zalecane rozwiązanie (dodatkowy wątek) omija wszelkie potrzeby asynchronicznych operacji we / wy.
MSalters
1
Tak, prb w próbce msdn jest taki, że rodzic utknął na zawsze w oczekiwaniu, w funkcji ReadFromPipe, line bSuccess = ReadFile (g_hChildStd_OUT_Rd, chBuf, BUFSIZE, & dwRead, NULL); odczyta 70 bajtów pierwszy raz, a drugi czas utknie na zawsze (PeekNamedPipe, którego brakuje, nie blokuje).
Fernando Gonzalez Sanchez
-2

Twoje preferencje dotyczące potoku danych Windows z serwera do okna dos klienta, natychmiast lub później, mogą być spełnione z małym dyskiem RAM. Ta sama pamięć jest przydzielana do danych, zapisywana / odczytywana z nazwą podobną do systemu plików. Klient usuwa zużyty plik i czeka na inny lub pozostawia go, aby zniknął po wyłączeniu komputera.

użytkownik999850
źródło