在Windows中,我可以在命令行中将stdout重定向到(命名的)管道吗?


17

有没有一种方法可以将Win32控制台中进程的标准输出重定向到命名管道?Windows中内置了命名管道,尽管它们是一个有用的概念,但我从未在命令行中使用过它们。

就是 喜欢example.exe >\\.\mypipe。(此语法可能不正确,但是您明白了。)我希望能够同时将stdoutstderr重定向到不同的管道。

我想避免使用物理文件来代替,避免处理IO速度慢,IO缓冲区,文件锁,访问权限,可用硬盘空间,覆盖决定,过分的持久性等。

另一个原因是因为传统的Windows 工具集没有像Unix那样围绕基于(文本)文件的原理进行设计。另外,如果有的话,命名管道也很难在Windows中安装。

最后,好奇的是,一个好的概念能否被很好地利用。


1
您是说要重定向到命名管道或邮筒?您是否愿意写接收端?
ixe013

是的,就像命名管道或邮筒一样。我还没有,但愿意写接收端。
n611x007 2012年

Answers:


8

我不确定为什么您不想重定向到文件。我将在此处提供两种方法。一种方法是重定向到文件或从文件读取,另一种是一组程序。


命名管道

我所做的是为.NET 4编写了两个程序。一个程序将输出发送到命名管道,另一个程序从该管道读取并显示到控制台。用法很简单:

asdf.exe | NamedPipeServer.exe "APipeName"

在另一个控制台窗口中:

NamedPipeClient.exe "APipeName"

不幸的是,由于Windows命令提示符中的管道运算符()的限制,它只能重定向stdout(或stdin合并),而不能单独重定向。如果您知道如何通过该管道操作员发送消息,它应该可以工作。或者,可以修改服务器以启动您的程序,并专门重定向。如果有必要,请在评论中让我知道(或您自己做);如果您具有一些C#和.NET“过程”库知识,这并不是很难。stderr|stderrstderr

您可以下载服务器客户端

如果在连接后关闭服务器,客户端将立即关闭。如果您在连接后关闭客户端,则服务器将在您尝试通过它发送邮件时立即关闭。重新连接断了的管道是不可能的,主要是因为我现在不愿意做那么复杂的事情。每个服务器也仅限于一个客户端

源代码

这些都是用C#编写的。试图解释它没有多大意义。他们使用.NET NamedPipeServerStreamNamedPipeClientStream

服务器:

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 '17

15

令我惊讶的是,这还没有得到正确的回答。系统确实存在一个UNC路径,该路径已分配给命名管道,可以在网络中的任何计算机上访问,可以像普通文件一样使用:

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

假设在此计算机上存在名为“ StdOutPipe”和“ StdErrPipe”的管道,这将尝试进行连接并对其进行写入。该pipe部分指定您要使用命名管道。


我认为这应该被标记为正确答案,因为这只是在问@ n611x007,不需要外部程序来执行此操作!
Arturn

问题是,您仍然需要启动某种服务来创建那些管道...。它们并不独立于所创建的程序而存在,并且在程序消失时被销毁。
Erik Aronesty

@ErikAronesty我认为这些管道已经存在。否则,将无法仅使用cmd.exe创建它们。
IllidanS4希望莫妮卡回到

是的,这是有关unix管道的很酷的事情,您可以从命令行创建管道
Erik Aronesty

1

没有标准外壳程序(CMD.EXE)。对于程序员来说,这很容易。只需抓住您启动的过程的两个管道即可。


1
唯一的prb是该示例使用匿名管道,该管道不支持重叠的io(异步),因此容易出现死锁,或者至少必须使用PeekNamedPipe。
费尔南多·冈萨雷斯·桑切斯2015年

1
阻塞等待不是死锁,而且根本的问题(数据消耗线程阻止生产者线程生产它)还是无法通过重叠的I / O解决。
MSalters


1
@FernandoGonzalezSanchez:几乎是同样的问题。请注意,推荐的解决方案(额外线程)回避了任何对异步I / O的需求。
MSalters

1
是的,msdn示例中的prb是父级永远卡住,在函数ReadFromPipe中,行bSuccess = ReadFile(g_hChildStd_OUT_Rd,chBuf,BUFSIZE,&dwRead,NULL); 第一次将读取70个字节,而第二次将永远卡住(丢失的PeekNamedPipe处于非阻塞状态)。
费尔南多·冈萨雷斯·桑切斯

-1

使用较小的RAM驱动器可能会满足您对立即从服务器到客户端dos窗口的Windows管道数据的偏好。将为数据分配相同的内存,并使用类似文件系统的名称进行读写。客户端要么删除用完的文件并等待另一个文件,要么在计算机关闭时使其消失。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.