ใน windows ฉันสามารถเปลี่ยนเส้นทาง stdout ไปยังไพพ์ (ชื่อ) ในบรรทัดคำสั่งได้หรือไม่


17

มีวิธีการเปลี่ยนเส้นทางการส่งออกมาตรฐานของกระบวนการในคอนโซล Win32 ไปป์ที่มีชื่อหรือไม่ ไพพ์เนมนั้นสร้างขึ้นใน Windows และในขณะที่มันเป็นแนวคิดที่มีประโยชน์ฉันไม่เคยเห็นมันมาใช้จากบรรทัดคำสั่ง

กล่าวคือ example.exe >\\.\mypipeเช่น (ไวยากรณ์นี้อาจไม่ถูกต้อง แต่คุณได้รับจุด) ฉันต้องการที่จะสามารถเปลี่ยนเส้นทางstdoutและstderrไปยังท่อที่แตกต่างกันในเวลาเดียวกัน

ฉันต้องการหลีกเลี่ยงการใช้ไฟล์ทางกายภาพแทนเพื่อหลีกเลี่ยงการจัดการกับความช้าของ IO บัฟเฟอร์ IO การล็อคไฟล์สิทธิ์การเข้าถึงพื้นที่ว่างบนฮาร์ดดิสก์ที่มีอยู่การตัดสินใจเขียนทับการคงอยู่อย่างไม่ตั้งใจ ฯลฯ

อีกเหตุผลหนึ่งเป็นเพราะแบบ Windows ชุดของเครื่องมือที่ไม่ได้ออกแบบมารอบ (ข้อความ) ตามปรัชญาไฟล์เท่าในขณะที่ระบบปฏิบัติการยูนิกซ์ นอกจากนี้ชื่อท่อไม่สามารถติดตั้งได้อย่างง่ายดายใน Windows ถ้าเลย

ในที่สุดก็มีความอยากรู้อยากเห็นหากแนวคิดที่ดีสามารถนำไปใช้ประโยชน์ได้


1
คุณหมายถึงต้องการเปลี่ยนเส้นทางไปยังไปป์ที่ระบุชื่อหรือ mailslot หรือไม่? คุณมี / เต็มใจที่จะเขียนจุดสิ้นสุดการรับหรือไม่?
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

15

ฉันประหลาดใจที่คำตอบนี้ยังไม่ได้ตอบอย่างถูกต้อง มีเส้นทาง UNC ที่ถูกกำหนดให้กับไพพ์ที่ชื่อโดยระบบซึ่งสามารถเข้าถึงได้บนเครื่องใด ๆ ในเครือข่ายซึ่งสามารถใช้งานได้เหมือนไฟล์ปกติ:

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

สมมติว่ามีท่อชื่อ "StdOutPipe" และ "StdErrPipe" อยู่ในเครื่องนี้ซึ่งจะพยายามเชื่อมต่อและเขียนไปยังพวกเขา pipeส่วนหนึ่งคือสิ่งที่ระบุว่าคุณต้องการไปป์ที่มีชื่อ


ฉันคิดว่าควรทำเครื่องหมายเป็นคำตอบที่ถูกต้องเนื่องจากเป็นเพียงสิ่งที่ถาม @ n611x007 ไม่ใช่โปรแกรมภายนอกที่จำเป็นสำหรับการทำเช่นนี้!
arturn

ปัญหาคือคุณยังต้องเปิดบริการบางประเภทที่สร้างไปป์เหล่านั้น .... พวกเขาไม่ได้อยู่อย่างอิสระจากโปรแกรมที่สร้างขึ้นและถูกทำลายเมื่อโปรแกรมหายไป
Erik Aronesty

@ErikAronesty ฉันคิดว่าท่อเหล่านี้มีอยู่แล้ว มิฉะนั้นจะไม่มีวิธีสร้างด้วย cmd.exe เท่านั้น
IllidanS4 ต้องการโมนิกากลับ

ใช่นั่นคือสิ่งที่ยอดเยี่ยมเกี่ยวกับท่อยูนิกซ์คุณสามารถสร้างท่อจากบรรทัดคำสั่ง
Erik Aronesty

1

ไม่ได้อยู่กับเชลล์มาตรฐาน (CMD.EXE) สำหรับโปรแกรมเมอร์ก็ค่อนข้างง่าย เพียงแค่คว้าสองท่อของกระบวนการที่คุณเริ่มต้น


1
prb เท่านั้นคือตัวอย่างใช้ไพพ์นิรนามซึ่งไม่รองรับ io ที่ทับซ้อนกัน (async) จึงมีแนวโน้มที่จะเกิด deadlocks หรืออย่างน้อยต้องใช้ PeekNamedPipe
เฟอร์นันโดกอนซาเลซซานเชซ

1
การรอการบล็อกไม่ใช่การหยุดชะงักและปัญหาพื้นฐาน (การใช้เธรดบล็อกที่ผู้ผลิตสร้างจากเธรด) จะไม่ถูกแก้ไขโดย I / O ที่ทับซ้อนกัน
MSalters

ฉันหมายถึงสิ่งนี้: blogs.msdn.com/b/oldnewthing/archive/2011/07/07/10183884.aspxตัวอย่างการหยุดชะงัก
เฟอร์นันโดกอนซาเลซซานเชซ

1
@ FernandoGonzalezSanchez: ปัญหาเดียวกันค่อนข้างมาก โปรดทราบว่าโซลูชันที่แนะนำ (เธรดพิเศษ) จะไม่ตอบสนองความต้องการ async I / O ใด ๆ
MSalters

1
ใช่ prb ในตัวอย่าง msdn คือพาเรนต์ติดอยู่ตลอดเวลารอในฟังก์ชัน ReadFromPipe, บรรทัด bSuccess = ReadFile (g_hChildStd_OUT_Rd, chBuf, BUFSIZE, & dwRead, NULL); จะอ่าน 70 ไบต์ครั้งที่ 1 และครั้งที่ 2 จะติดตลอดกาล (PeekNamedPipe ซึ่งหายไปไม่ใช่การปิดกั้น)
เฟอร์นันโดกอนซาเลซซานเชซ

-1

การกำหนดค่าตามความชอบของคุณสำหรับไพพ์ข้อมูลของ Windows จากเซิร์ฟเวอร์ไปยังหน้าต่างไคลเอนต์ dos ทั้งในทันทีหรือหลังจากนั้นอาจพอใจกับไดรฟ์ RAM ขนาดเล็ก หน่วยความจำเดียวกันได้รับการจัดสรรสำหรับข้อมูลเขียน / อ่านโดยใช้ชื่อเหมือนระบบไฟล์ ไคลเอ็นต์จะลบไฟล์ที่ใช้แล้วและรอไฟล์อื่นหรือปล่อยให้ไฟล์นั้นหายไปเมื่อคอมพิวเตอร์ปิดเครื่อง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.