У вікнах можна перенаправити stdout на (іменовану) трубу в командному рядку?


16

Чи є спосіб перенаправити стандартний вихід процесу в Росії Консоль Win32 до a названа труба ? Названі труби вбудовані в Windows, і хоча вони були б корисною концепцією, я ніколи не бачив, як вони використовуються з командного рядка.

Тобто. люблю example.exe >\\.\mypipe. (Цей синтаксис може бути неправильним, але ви отримаєте точку.) Я хотів би мати можливість перенаправляти stdout і stderr до різних труб одночасно.

Я хотів би уникати використання фізичних файлів як замінника, щоб уникнути боротьби з IO slowness, IO буферів, блокування файлів, права доступу, доступного місця на жорсткому диску, рішення переписати, неміцна наполегливість і т.д.

Інша причина полягає в тому, що традиційні Windows набір інструментів не розроблені навколо філософії, заснованої на (текстовому) файлі як у Unix . Крім того, названі труби не могли бути легко встановлені в Windows, якщо взагалі.

Нарешті, існує цікавість, якщо хороша концепція може бути використана.


1
Ви маєте на увазі перенаправлення на іменовану трубку або лист поштою? У вас є / є бажання писати одержувач?
ixe013

Так, подібно до іменованої труби або листування поштою. Я ще не маю, але готовий написати записуючий кінець.
n611x007

Відповіді:


8

Я не впевнений, чому ви не хочете перенаправляти файл. Існують два способи, які я наведу тут. Один з методів полягає в тому, щоб перенаправляти файл і читати з нього, інший - це набір програм.


Названі труби

Те, що я зробив, було написання двох програм для .NET 4. Один надсилає вивід на іменовану трубку, інший читає з цієї труби і виводить на консоль. Використання досить просте:

asdf.exe | NamedPipeServer.exe "APipeName"

В іншому вікні консолі:

NamedPipeClient.exe "APipeName"

На жаль, це може лише перенаправити stdout (або stdinабо комбіновано) stderr самостійно, через обмеження в операторі труби ( | ) у командному рядку Windows. Якщо ви зрозумієте, як відправити stderr через цього оператора труби він повинен працювати. Крім того, сервер може бути змінений, щоб запустити вашу програму та спеціально перенаправити stderr. Якщо це необхідно, дайте мені знати в коментарі (або зробіть це самостійно); це не надто складно, якщо ви маєте знання бібліотеки C # та .NET "Процес".

Ви можете завантажити сервер і клієнта .

Якщо закрити сервер після з'єднання, клієнт негайно закриється. Якщо ви закриєте клієнт після з'єднання, сервер закриється, як тільки ви спробуєте щось надіслати через нього. Неможливо повторно підключити розбиту трубку, головним чином тому, що я не можу турбуватися про те, що зараз так складно. Це також обмежено один клієнт на сервер .

Вихідний код

Вони написані на C #. Тут нема чого говорити про це. Вони використовують .NET NamedPipeServerStream і NamedPipeClientStream .

Сервер:

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

Клієнт:

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

Перенаправлення до файлу

type NUL>StdErr.temp
start powershell -c Get-Content StdErr.temp -Wait
MyExecutable.exe 2>StdErr.temp
  1. Створіть порожній файл
  2. Запустіть нове вікно консолі, яке слідкує за файлом
  3. Виконати виконуваний файл і перенаправити stderr Вивести цей файл

Це забезпечує бажаний ефект перегляду одного вікна консолі stdout (і надавати stdin ), а інший дивитися stderr.

Все, що імітує tail буде працювати. Метод PowerShell працює у Windows, але може бути трохи повільним (тобто існує деяка затримка між записом до файлу та відображенням на екран). Подивитися це питання StackOverflow для інших tail альтернатив.

Єдина проблема - тимчасовий файл може стати досить великим. Можливим обхідним шляхом є запуск циклу, який друкується, лише якщо файл має вміст і очищає файл відразу після цього, але це може спричинити стан гонки.


2
Користувачі UNIX - це ті, які роздратовані, оскільки Windows знову не в змозі реалізувати 40-річну ідею розумним чином. Вам не потрібно писати спеціальну програму щоразу, коли ви хочете зробити основну річ. facepalm
bambams

Див. Нижче: можна використовувати шлях UNC, призначений для іменованої труби, і отримати доступ до нього безпосередньо.
Erik Aronesty

11

Я дивуюсь, що на це вже правильно не відповіли. Це дійсно є Шлях UNC призначені для іменних труб системою, доступною на будь-якій машині в мережі, яка може використовуватися як звичайний файл:

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

Припускаючи, що на цій машині існують труби з назвою "StdOutPipe" і "StdErrPipe", це намагається підключити і записати до них. The pipe частина є те, що вказує, що ви хочете назвати трубку.


Я думаю, що це має бути позначено як правильну відповідь, так як це тільки те, що запитує @ n611x007, а зовнішні програми не потрібні для цього!
arturn

проблема полягає в тому, що вам все одно потрібно запустити певну послугу, яка створює ці труби .... вони не існують незалежно від програми, яка створена і руйнується, коли програма йде.
Erik Aronesty

@ErikAronesty Я вважав, що ці труби вже існують. Інакше їх можна створити лише за допомогою cmd.exe.
IllidanS4

Так, це цікава річ про труби Unix, ви можете створювати труби з командного рядка
Erik Aronesty

1

Не з стандартною оболонкою (CMD.EXE). Для програмістів це досить легко . Просто схопіть дві труби процесу, який ви почали.


1
Тільки prb полягає в тому, що зразок використовує анонімні труби, які не підтримують перекриття io (async) і таким чином схильні до тупиків, або принаймні PeekNamedPipe.
Fernando Gonzalez Sanchez

1
Блокування очікувань не є тупиками, і фундаментальна проблема (потік, що споживає дані, що блокує виробничий потік від її виробництва), не вирішується шляхом перекриття вводу-виводу.
MSalters

Я мав на увазі це: blogs.msdn.com/b/oldnewthing/archive/2011/07/07/10183884.aspx , приклад з тупиковою ситуацією.
Fernando Gonzalez Sanchez

1
@FernandoGonzalezSanchez: Практично така ж проблема. Зауважимо, що рекомендоване рішення (додатковий потік) відхиляє будь-яку потребу в async I / O.
MSalters

1
Так, prb у зразку msdn є тим, що батько застряг назавжди в очікуванні, у функції ReadFromPipe, лінія bSuccess = ReadFile (g_hChildStd_OUT_Rd, chBuf, BUFSIZE, & amp; dwRead, NULL); буде читати 70 байтів 1 раз, і 2-й раз застрягне назавжди (PeekNamedPipe, який відсутній, не блокує).
Fernando Gonzalez Sanchez
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.