В Windows я могу перенаправить стандартный вывод в (именованный) канал в командной строке?

17

Есть ли способ перенаправить стандартный вывод процесса в консоли Win32 на именованный канал ? Именованные каналы встроены в Windows, и хотя они были бы полезны, я никогда не видел, чтобы они использовались из командной строки.

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

Я хотел бы избежать использования физических файлов в качестве замены, избегая работы с медлительностью ввода-вывода, буферами ввода-вывода, блокировками файлов, правами доступа, доступным пространством на жестком диске, решением о перезаписи, постоянным сохранением и т. Д.

Другая причина заключается в том, что традиционный набор инструментов для Windows не основан на философии, основанной на текстовых файлах, а на Unix . Кроме того, именованные каналы не могли быть легко установлены в Windows, если вообще.

Наконец, есть любопытство, если хорошая концепция может быть использована в хорошем смысле.

n611x007
источник
1
Вы имеете в виду, как перенаправление на именованный канал или почтовый ящик? Есть ли у вас / готовы написать получающий конец?
ixe013
Да, как для именованного канала или почтового ящика. У меня пока нет, но я хочу написать получающий конец.
n611x007

Ответы:

8

Я не уверен, почему вы не хотите перенаправить в файл. Здесь я приведу два метода. Один метод - перенаправление и чтение из файла, другой - набор программ.


Именованные трубы

Я написал две программы для .NET 4. Одна отправляет вывод в именованный канал, другая читает из этого канала и выводит на консоль. Использование довольно просто:

asdf.exe | NamedPipeServer.exe "APipeName"

В другом окне консоли:

NamedPipeClient.exe "APipeName"

К сожалению, это может только перенаправить stdout(или stdin, или объединить), но не stderrсамо по себе, из-за ограничений в трубе оператора ( |) в командной строке Windows. Если вы выясните, как отправить stderrчерез этого оператора трубопровода, это должно работать. Кроме того, сервер может быть изменен для запуска вашей программы и, в частности, для перенаправления stderr. Если это необходимо, дайте мне знать в комментарии (или сделайте это самостоятельно); это не так уж сложно, если у вас есть знания по библиотекам C # и .NET "Process".

Вы можете скачать сервер и клиент .

Если вы закроете сервер после подключения, клиент закроется немедленно. Если вы закроете клиент после подключения, сервер закроется, как только вы попытаетесь что-то отправить через него. Невозможно восстановить сломанную трубу, главным образом потому, что я не могу быть обеспокоен тем, чтобы сделать что-то настолько сложное прямо сейчас. Это также ограничено одним клиентом на сервер .

Исходный код

Они написаны на 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-путь, назначенный для именованного канала, и получить к нему прямой доступ.
Эрик Аронесты
15

Я удивлен, что на этот вопрос уже не ответили правильно. В действительности существует UNC-путь, назначенный именованным каналам системой, доступный на любом компьютере в сети, который можно использовать как обычный файл:

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

Предполагая, что каналы с именами «StdOutPipe» и «StdErrPipe» существуют на этом компьютере, это попытается соединиться и записать их. pipeЧасть того, что определяет , что вы хотите именованный канал.

IllidanS4 хочет вернуть Монику
источник
Я думаю, что это должно быть помечено как правильный ответ, поскольку это именно то, что спрашивает @ n611x007, для этого не требуются внешние программы!
17
проблема в том, что вам все еще нужно запустить какой-то сервис, который создает эти каналы .... они не существуют независимо от созданной программы и уничтожаются при ее выходе.
Эрик Аронесты
@ErikAronesty Я предположил, что эти трубы уже существуют. В противном случае невозможно создать их только с помощью cmd.exe.
IllidanS4 хочет вернуть Монику
Да, это крутая вещь о каналах Unix, вы можете создавать каналы из командной строки
Эрик Аронесты
1

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

MSalters
источник
1
Единственное, что указывает на то, что в примере используются анонимные каналы, которые не поддерживают перекрывающийся интерфейс ввода-вывода (асинхронный) и, следовательно, склонны к взаимоблокировкам, или, по крайней мере, должен использоваться PeekNamedPipe.
Фернандо Гонсалес Санчес
1
Ожидания блокировки не являются взаимоблокировками, и фундаментальная проблема (поток данных блокирует поток производителя от его создания) так или иначе не решается с помощью перекрывающегося ввода-вывода.
MSalters
Я имел в виду это: blogs.msdn.com/b/oldnewthing/archive/2011/07/07/10183884.aspx , пример тупика.
Фернандо Гонсалес Санчес
1
@FernandoGonzalezSanchez: Почти та же проблема. Обратите внимание, что рекомендуемое решение (дополнительная нить) позволяет избежать асинхронного ввода-вывода.
MSalters
1
Да, prb в образце msdn - родитель застрял навсегда в функции ReadFromPipe, строка bSuccess = ReadFile (g_hChildStd_OUT_Rd, chBuf, BUFSIZE, & dwRead, NULL); 1 раз прочитает 70 байт, а 2 раза застрянет навсегда (PeekNamedPipe, который отсутствует, не блокируется).
Фернандо Гонсалес Санчес
-1

Ваши предпочтения для канала передачи данных Windows от сервера к клиентскому окну сразу или позже могут быть удовлетворены небольшим ОЗУ. Эта же память выделяется для данных, записываемых / читаемых с именем, похожим на файловую систему. Клиент либо удаляет использованный файл и ждет другого, либо оставляет его исчезнуть при выключении компьютера.

user999850
источник