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

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

有没有办法重定向标准输出一个过程Win32 控制台命名管道? 命名管道是 Windows 的内置功能,虽然它们是一个有用的概念,但我从未见过在命令行中使用它们。

即像example.exe >\\.\mypipe. (这个语法可能不正确,但你明白我的意思。)我希望能够重定向标准输出标准错误可同时传输至不同的管道。

我想避免使用物理文件作为替代品,以避免处理 IO 缓慢、IO 缓冲区、文件锁、访问权限、可用硬盘空间、覆盖决定、无意持久性等问题。

另一个原因是,传统的 Windows工具集并不是围绕基于(文本)文件的哲学进行设计的就像在 Unix 中一样. 此外,命名管道无法轻易地在 Windows 中安装,甚至根本无法安装。

最后,人们好奇一个好的概念是否能够得到很好的利用。

答案1

我不确定你为什么不想重定向到一个文件。我在这里提供两种方法。一种方法是重定向到一个文件并从中读取,另一种方法是一组程序。


命名管道

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

asdf.exe | NamedPipeServer.exe "APipeName"

在另一个控制台窗口中:

NamedPipeClient.exe "APipeName"

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

您可以下载服务器客户

如果在连接后关闭服务器,客户端也会立即关闭。如果在连接后关闭客户端,服务器会在您尝试通过它发送任何内容时立即关闭。无法重新连接破裂的管道,主要是因为我现在懒得做这么复杂的事情。它也仅限于每个服务器一个客户端

源代码

这些是用 C# 编写的。解释起来没什么意义。它们使用 .NET命名管道服务器流命名管道客户端流

服务器:

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

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

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

假设此机器上存在名为“StdOutPipe”和“StdErrPipe”的管道,这将尝试连接并写入它们。该pipe部分指定您需要一个命名管道。

答案3

您希望 Windows 数据管道从服务器立即或稍后传输到客户端 DOS 窗口,而小型 RAM 驱动器可能会满足您的这一需求。相同的内存分配给数据,使用类似文件系统的名称进行写入/读取。客户端要么删除用完的文件并等待另一个文件,要么在计算机关闭时让它消失。

相关内容