Hai chương trình có StdIn và StdOut gắn liền với nhau


12

Giả sử tôi có hai chương trình được gọi ProgramAProgramB. Tôi muốn chạy cả hai đồng thời trong trình thông dịch cmd Windows. Nhưng tôi muốn StdOutcác ProgramAcuốn hút vào StdIncủa ProgramBStdOutcủa ProgramBnối với StdIncác ProgramA.

Một cái gì đó như thế này

 ______________
| | | |
| StdIn (== ← === ← == (StdOut |
| Chương trình A | | Chương trình B |
| | | |
| StdOut) == → === → ==) StdIn |
| ______________ | | ______________ |

Có lệnh nào để làm điều này không - một số cách để đạt được chức năng này từ cmd?


4
Trên unix tôi sử dụng các ống có tên, để làm điều này, các cửa sổ có một cái gì đó được gọi là các ống có tên hoàn toàn khác nhau và có thể không áp dụng được.
Jasen

@Jasen theo một nhận xét ở đây linuxjournal.com/article/2156 các ống có tên có thể hoạt động trên cygwin .. nếu vậy thì có lẽ bạn có thể giải thích và đăng dưới dạng câu trả lời, mặc dù đáng thử nghiệm trên cygwin trước
barlop 16/07/2016

Tôi cho rằng các chương trình cũng sẽ phải được viết để không lặp .. Vì vậy, nếu thiết bị xuất chuẩn là một dòng trống thì đừng cung cấp cho stdin và nếu không có gì để stdin thì hãy thoát. Như vậy tránh một vòng lặp vô hạn? Nhưng ngoài quan tâm, ứng dụng là gì?
barlop

2
nói rằng bạn muốn có hai trường hợp "Eliza" có một cuộc trò chuyện?
Jasen 16/07/2016

Nếu các chương trình không được thiết kế cẩn thận để xử lý việc này, chúng có thể gặp bế tắc - cả hai đang chờ đầu vào từ bên kia và không có gì xảy ra (hoặc cả hai đều cố gắng ghi vào bộ đệm đầy đủ và không bao giờ đọc bất cứ điều gì để làm trống chúng )
Random832

Câu trả lời:


5

Nó không chỉ có thể được thực hiện, nó có thể được thực hiện mà không có gì ngoài một tệp bó! :-)

Vấn đề có thể được giải quyết bằng cách sử dụng tệp tạm thời làm "đường ống". Giao tiếp hai chiều yêu cầu hai tệp "ống".

Quá trình A đọc stdin từ "pipe1" và ghi stdout vào "pipe2"
Process B đọc stdin từ "pipe2" và ghi stdout vào "pipe1"

Điều quan trọng là cả hai tệp tồn tại trước khi khởi chạy một trong hai quá trình. Các tập tin nên trống khi bắt đầu.

Nếu một tệp bó cố gắng đọc từ một tệp xảy ra ở cuối hiện tại, nó chỉ đơn giản trả về không có gì và tệp vẫn mở. Vì vậy, thói quen readLine của tôi liên tục đọc cho đến khi nó nhận được một giá trị không trống.

Tôi muốn có thể đọc và viết một chuỗi trống, vì vậy thói quen writeLine của tôi sẽ thêm một ký tự phụ mà readLine loại bỏ.

Quá trình A của tôi kiểm soát dòng chảy. Nó khởi tạo mọi thứ bằng cách viết 1 (tin nhắn cho B), sau đó vào một vòng lặp với 10 lần lặp trong đó nó đọc một giá trị (tin nhắn từ B), thêm 1, sau đó viết kết quả (tin nhắn cho B). Cuối cùng, nó chờ tin nhắn cuối cùng từ B, rồi viết tin nhắn "thoát" cho B và thoát ra.

Quá trình B của tôi nằm trong một vòng lặp vô điều kiện có thể đọc một giá trị (tin nhắn từ A), thêm 10 và sau đó ghi kết quả (tin nhắn cho A). Nếu B từng đọc tin nhắn "thoát" thì nó sẽ chấm dứt ngay lập tức.

Tôi muốn chứng minh rằng giao tiếp hoàn toàn đồng bộ, vì vậy tôi đưa ra độ trễ trong cả hai vòng xử lý A và B.

Lưu ý rằng thủ tục readLine nằm trong một vòng lặp chặt chẽ liên tục lạm dụng cả CPU và hệ thống tệp trong khi nó chờ đầu vào. Một độ trễ PING có thể được thêm vào vòng lặp, nhưng sau đó các quy trình sẽ không được đáp ứng.

Tôi sử dụng một đường ống thực sự như một sự thuận tiện để khởi chạy cả hai quá trình A và B. Nhưng đường ống là không hoạt động trong đó không có giao tiếp đi qua nó. Tất cả thông tin liên lạc là thông qua các tập tin "ống" tạm thời của tôi.

Tôi cũng có thể đã sử dụng START / B để khởi chạy các tiến trình, nhưng sau đó tôi phải phát hiện khi cả hai chấm dứt để tôi biết khi nào nên xóa các tệp "ống" tạm thời. Nó đơn giản hơn nhiều để sử dụng đường ống.

Tôi đã chọn đặt tất cả mã vào một tệp duy nhất - tập lệnh chính khởi chạy A và B, cũng như mã cho A và B. Tôi có thể đã sử dụng một tệp tập lệnh riêng cho mỗi quy trình.

kiểm tra

@echo off

if "%~1" equ "" (

    copy nul pipe1.txt >nul
    copy nul pipe2.txt >nul

    "%~f0" A <pipe1.txt >>pipe2.txt | "%~f0" B <pipe2.txt >>pipe1.txt

    del pipe1.txt pipe2.txt

    exit /b

)


setlocal enableDelayedExpansion
set "prog=%~1"
goto !prog!


:A
call :writeLine 1
for /l %%N in (1 1 5) do (
  call :readLine
    set /a ln+=1
  call :delay 1
    call :writeLine !ln!
)
call :readLine
call :delay 1
call :writeLine quit
exit /b


:B
call :readLine
if !ln! equ quit exit /b
call :delay 1
set /a ln+=10
call :writeLine !ln!
goto :B


:readLine
set "ln="
set /p "ln="
if not defined ln goto :readLine
set "ln=!ln:~0,-1!"
>&2 echo !prog!  reads !ln!
exit /b


:writeLine
>&2 echo !prog! writes %*
echo(%*.
exit /b


:delay
setlocal
set /a cnt=%1+1
ping localhost /n %cnt% >nul
exit /b

--OUTPUT--

C:\test>test
A writes 1
B  reads 1
B writes 11
A  reads 11
A writes 12
B  reads 12
B writes 22
A  reads 22
A writes 23
B  reads 23
B writes 33
A  reads 33
A writes 34
B  reads 34
B writes 44
A  reads 44
A writes 45
B  reads 45
B writes 55
A  reads 55
A writes 56
B  reads 56
B writes 66
A  reads 66
A writes quit
B  reads quit

Cuộc sống dễ dàng hơn một chút với ngôn ngữ cấp cao hơn. Dưới đây là một ví dụ sử dụng VBScript cho các quy trình A và B. Tôi vẫn sử dụng hàng loạt để khởi chạy các quy trình. Tôi sử dụng một phương pháp rất hay được mô tả tại Có thể nhúng và thực thi VBScript trong một tệp bó mà không cần sử dụng tệp tạm thời không? để nhúng nhiều tập lệnh VBS trong một tập lệnh bó.

Với ngôn ngữ cao hơn như VBS, chúng tôi có thể sử dụng một đường ống bình thường để truyền thông tin từ A đến B. Chúng tôi chỉ cần một tệp "ống" tạm thời duy nhất để chuyển thông tin từ B trở lại A. Bởi vì bây giờ chúng tôi có một ống hoạt động, A quá trình không cần gửi tin nhắn "thoát" đến B. Quá trình B chỉ đơn giản là vòng lặp cho đến khi đến cuối tập tin.

Nó chắc chắn là tốt đẹp khi có quyền truy cập vào một chức năng ngủ thích hợp trong VBS. Điều này cho phép tôi dễ dàng giới thiệu một độ trễ ngắn trong chức năng readLine để cho CPU nghỉ ngơi.

Tuy nhiên, có một nếp nhăn trong readLIne. Lúc đầu, tôi nhận được những thất bại không liên tục cho đến khi tôi nhận ra rằng đôi khi readLine sẽ phát hiện thông tin có sẵn trên stdin và ngay lập tức sẽ cố gắng đọc dòng trước khi B có cơ hội viết xong dòng này. Tôi đã giải quyết vấn đề bằng cách đưa ra một độ trễ ngắn giữa bài kiểm tra cuối tập tin và bài đọc. Một sự chậm trễ 5 ms dường như là một mánh khóe đối với tôi, nhưng tôi đã nhân đôi con số đó lên 10 ms chỉ để ở bên an toàn. Điều rất thú vị là lô không bị vấn đề này. Chúng tôi đã thảo luận ngắn gọn về điều này (5 bài viết ngắn) tại http://www.dostips.com/forum/viewtopic.php?f=3&t=7078#p47432 .

<!-- : Begin batch script
@echo off
copy nul pipe.txt >nul
cscript //nologo "%~f0?.wsf" //job:A <pipe.txt | cscript //nologo "%~f0?.wsf" //job:B >>pipe.txt
del pipe.txt
exit /b


----- Begin wsf script --->
<package>

<job id="A"><script language="VBS">

  dim ln, n, i
  writeLine 1
  for i=1 to 5
    ln = readLine
    WScript.Sleep 1000
    writeLine CInt(ln)+1
  next
  ln = readLine

  function readLine
    do
      if not WScript.stdin.AtEndOfStream then
        WScript.Sleep 10 ' Pause a bit to let B finish writing the line
        readLine = WScript.stdin.ReadLine
        WScript.stderr.WriteLine "A  reads " & readLine
        exit function
      end if
      WScript.Sleep 10 ' This pause is to give the CPU a break
    loop
  end function

  sub writeLine( msg )
    WScript.stderr.WriteLine "A writes " & msg
    WScript.stdout.WriteLine msg
  end sub

</script></job>

<job id="B"> <script language="VBS">

  dim ln, n
  do while not WScript.stdin.AtEndOfStream
    ln = WScript.stdin.ReadLine
    WScript.stderr.WriteLine "B  reads " & ln
    n = CInt(ln)+10
    WScript.Sleep 1000
    WScript.stderr.WriteLine "B writes " & n
    WScript.stdout.WriteLine n
  loop

</script></job>

</package>

Đầu ra giống như với giải pháp lô thuần túy, ngoại trừ dòng "thoát" cuối cùng không có ở đó.


4

Lưu ý- Nhìn lại, đọc lại câu hỏi, điều này không làm những gì được hỏi. Vì, trong khi nó liên kết hai quá trình với nhau, (theo một cách thú vị thậm chí sẽ hoạt động trên một mạng!), Nó không liên kết cả hai cách.


Tôi hy vọng bạn nhận được một vài câu trả lời cho điều này.

Đây là câu trả lời của tôi nhưng đừng chấp nhận nó, hãy chờ câu trả lời khác, tôi rất muốn xem một số câu trả lời khác.

Điều này đã được thực hiện từ cygwin. Và sử dụng lệnh 'nc' (thông minh). 'Wc -l' chỉ đếm các dòng. Vì vậy, tôi đang liên kết bất cứ điều gì hai lệnh, trong trường hợp này là echo và wc, sử dụng nc.

Lệnh bên trái đã được thực hiện đầu tiên.

nc là một lệnh có khả năng a) tạo một máy chủ hoặc b) kết nối như lệnh telnet ở chế độ thô, với một máy chủ. Tôi đang sử dụng cách sử dụng 'a' trong lệnh bên trái và cách sử dụng 'b' trong lệnh bên phải.

Vì vậy, nc ngồi đó lắng nghe chờ đợi một đầu vào, và sau đó sẽ dẫn đầu vào đó wc -lvà đếm các dòng và xuất số lượng dòng được nhập.

Sau đó, tôi đã chạy dòng để lặp lại một số văn bản và gửi dữ liệu đó đến 127.0.0.1:123 là máy chủ được đề cập.

nhập mô tả hình ảnh ở đây

Bạn có thể sao chép lệnh nc.exe từ cygwin và thử sử dụng tệp đó và tệp cygwin1.dll mà nó cần trong cùng thư mục. Hoặc bạn có thể làm điều đó từ chính Cygwin như tôi có. Tôi không thấy nc.exe trong gnuwin32. Họ có một tìm kiếm http://gnuwin32.sourceforge.net/ và nc hoặc netcat không xuất hiện. Nhưng bạn có thể nhận được cygwin https://cygwin.com/install.html


Tôi đã mất mười lần để đọc nó trước khi tôi hiểu chuyện gì đang xảy ra ..... điều đó khá thông minh
DarthRubik

@DarthRubik yeah, và bạn có thể sử dụng netstat -aon | find ":123"để thấy rằng lệnh bên trái đã tạo ra máy chủ
barlop

Nhưng làm thế nào để bạn giao tiếp theo hướng khác (tức là từ phía wcsau đến echolệnh).
DarthRubik

Khi tôi cố gắng với nc để thực hiện cả hai cách tôi không thể làm cho nó hoạt động, có thể nc đi vào một vòng lặp nào đó.
barlop

nc -lp9999 | prog1 | prog2 | nc 127.0.0.1 9999các bước có thể cần phải được thực hiện để đảm bảo bộ đệm được xả kịp thời
Jasen 16/07/2016

2

Một hack (tôi không muốn làm điều này, nhưng đây là những gì tôi sẽ làm bây giờ) là viết một ứng dụng C # để làm điều này cho bạn. Tôi đã không triển khai một số tính năng chính trong chương trình này (như thực sự sử dụng các đối số được cung cấp cho tôi), nhưng đây là:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;


namespace Joiner
{
    class Program
    {
        static Process A;
        static Process B;
        static void AOutputted(object s, DataReceivedEventArgs a)
        {
            Console.WriteLine("A:" + a.Data);
            //Console.WriteLine("Sending to B");
            B.StandardInput.WriteLine(a.Data);
        }
        static void BOutputted(object s, DataReceivedEventArgs a)
        {
            Console.WriteLine("B:" + a.Data);
            //Console.WriteLine("Sending to A");
            A.StandardInput.WriteLine(a.Data);
        }
        static void Main(string[] args)
        {

            A = new Process();
            B = new Process();
            A.StartInfo.FileName = "help";
            B.StartInfo.FileName = "C:\\Users\\Owner\\Documents\\Visual Studio 2010\\Projects\\Joiner\\Test\\bin\\Debug\\Test.exe";

            A.StartInfo.Arguments = "mkdir";
            //B.StartInfo.Arguments = "/E /K type CON";

            A.StartInfo.UseShellExecute = false;
            B.StartInfo.UseShellExecute = false;

            A.StartInfo.RedirectStandardOutput = true;
            B.StartInfo.RedirectStandardOutput = true;

            A.StartInfo.RedirectStandardInput = true;
            B.StartInfo.RedirectStandardInput = true;

            A.OutputDataReceived += AOutputted;
            B.OutputDataReceived += BOutputted;

            A.Start();
            B.Start();

            A.BeginOutputReadLine();
            B.BeginOutputReadLine();



            while (!A.HasExited || !B.HasExited) { }
            Console.ReadLine();

        }
    }
}

Cuối cùng, khi chương trình này có đầy đủ chức năng và mã gỡ lỗi bị loại bỏ, bạn sẽ sử dụng nó như thế này:

joiner "A A's args" "B B's Args"
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.