StdIn과 StdOut을 묶은 두 프로그램


12

ProgramAand 라고하는 두 개의 프로그램이 있다고 가정합니다 ProgramB. Windows cmd 인터프리터에서 동시에 두 가지를 모두 실행하고 싶습니다. 그러나 내가 원하는 StdOutProgramA받는 후크를 StdInProgramBStdOutProgramB받는 후크 StdInProgramA.

이 같은

 ________________ ________________
| | | |
| StdIn (== ← === ← == (StdOut |
| 프로그램 A | | 프로그램 B |
| | | |
| StdOut) == → === → ==) StdIn |
| ________________ | | ________________ |

이것을 수행하는 명령이 있습니까-cmd 에서이 기능을 달성하는 방법은 무엇입니까?


4
유닉스에서는 명명 된 파이프를 사용합니다.이 작업을 수행하기 위해 Windows에는 완전히 다르고 적용 할 수없는 명명 된 파이프라는 것이 있습니다.
Jasen

여기에 코멘트에 따라 @Jasen linuxjournal.com/article/2156 그럼 아마 당신은 정교하고 답변으로 게시 할 수있는 경우 첫 번째 Cygwin에서의 가치가 테스트하지만 .. Cygwin에서에서 작동 할 수 있습니다 명명 된 파이프를
barlop

나는 또한 프로그램이 루프되지 않도록 작성되어야한다고 가정합니다. 따라서 stdout이 빈 줄이면 stdin에 피드하지 말고 stdin에 아무것도 없으면 종료하십시오. 따라서 무한 루프를 피합니까? 그러나 관심이 없다면 응용 프로그램은 무엇입니까?
barlop

2
"Eliza"의 두 인스턴스가 대화를하고 싶다고 말합니까?
Jasen

프로그램이 이것을 처리하도록 신중하게 설계되지 않은 경우 교착 상태에 빠질 수 있습니다. 둘 다 다른 입력을 기다리지 않고 아무 일도 일어나지 않습니다. )
Random832

답변:


5

그것은 할 수있을뿐만 아니라 배치 파일만으로 할 수 있습니다! :-)

임시 파일을 "파이프"로 사용하면이 문제를 해결할 수 있습니다. 양방향 통신에는 두 개의 "파이프"파일이 필요합니다.

프로세스 A는 "pipe1"에서 stdin을 읽고 stdout을 "pipe2"에 씁니다.
프로세스 B는 "pipe2"에서 stdin을 읽고 stdout를 "pipe1"에 씁니다.

프로세스를 시작하기 전에 두 파일이 모두 존재해야합니다. 시작시 파일이 비어 있어야합니다.

배치 파일이 현재 끝에있는 파일에서 읽으려고하면 아무 것도 반환하지 않고 파일은 열린 상태로 유지됩니다. 따라서 내 readLine 루틴은 비어 있지 않은 값을 얻을 때까지 지속적으로 읽습니다.

빈 문자열을 읽고 쓸 수 있기를 원하므로 writeLine 루틴이 readLine이 제거하는 추가 문자를 추가합니다.

내 A 프로세스는 흐름을 제어합니다. 1 (메시지 B에 메시지)을 작성하여 일을 시작한 다음 10 개의 반복으로 루프를 시작하여 값 (B의 메시지)을 읽고 1을 추가 한 다음 결과를 씁니다 (메시지 B). 마지막으로 B에서 마지막 메시지를 기다린 다음 "quit"메시지를 B에 쓰고 종료합니다.

내 B 프로세스는 값 (A의 메시지)을 읽고 10을 더한 다음 결과를 (메시지 A) 쓰는 조건부 끝없는 루프에 있습니다. B가 "quit"메시지를 읽으면 즉시 종료됩니다.

통신이 완전히 동기화되어 있음을 증명하고 싶었으므로 A 및 B 프로세스 루프 모두에서 지연이 발생했습니다.

readLine 프로시 저는 입력을 기다리는 동안 CPU와 파일 시스템을 지속적으로 악용하는 긴밀한 루프 상태입니다. 루프에 PING 지연을 추가 할 수 있지만 프로세스가 응답하지 않습니다.

A와 B 프로세스를 모두 시작하기 위해 편의상 진정한 파이프를 사용합니다. 그러나 파이프가 통신을 통과하지 않는다는 점에서 파이프가 작동하지 않습니다. 모든 통신은 임시 "파이프"파일을 통해 이루어집니다.

프로세스를 시작하기 위해 START / B를 사용할 수도 있었지만 임시 "파이프"파일을 삭제할시기를 알 수 있도록 프로세스가 모두 종료되는 시점을 감지해야했습니다. 파이프를 사용하는 것이 훨씬 간단합니다.

모든 코드를 단일 파일 (A 및 B를 시작하는 마스터 스크립트와 A 및 B 용 코드)에 넣기로 결정했습니다. 각 프로세스마다 별도의 스크립트 파일을 사용할 수있었습니다.

test.bat

@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

--산출--

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

더 높은 수준의 언어로 인생은 조금 더 쉽습니다. 아래는 A 및 B 프로세스에 VBScript를 사용하는 예입니다. 여전히 배치를 사용하여 프로세스를 시작합니다. 임시 파일을 사용하지 않고 배치 파일 내에 VBScript를 포함하고 실행할 수 있습니까?에 설명 된 매우 멋진 방법 을 사용합니다. 단일 배치 스크립트 내에 여러 VBS 스크립트를 포함합니다.

VBS와 같은 더 높은 언어를 사용하면 일반 파이프를 사용하여 A에서 B로 정보를 전달할 수 있습니다. B에서 A로 정보를 다시 전달하려면 하나의 임시 "파이프"파일 만 있으면됩니다. 이제 기능 파이프 인 A가 있으므로 프로세스는 "quit"메시지를 B로 보낼 ​​필요가 없습니다. B 프로세스는 파일 끝에 도달 할 때까지 단순히 루프됩니다.

VBS에서 적절한 절전 기능에 액세스하는 것이 좋습니다. 이렇게하면 readLine 함수에 짧은 지연을 쉽게 도입하여 CPU가 중단 될 수 있습니다.

그러나 readLIne 내에 하나의 주름이 있습니다. 처음에는 때때로 readLine이 stdin에서 정보를 사용할 수 있음을 발견하고 B가 줄 쓰기를 마치기 전에 즉시 줄을 읽으려고 할 때까지 간헐적 오류가 발생했습니다. 파일 끝 테스트와 읽기 사이에 짧은 지연을 도입하여 문제를 해결했습니다. 5msec의 지연은 나를 위해 속임수처럼 보이지만, 나는 안전한면에 있기 위해 10msec로 두 배로 늘 렸습니다. 배치가이 문제를 겪지 않는다는 것은 매우 흥미 롭습니다. 우리는 http://www.dostips.com/forum/viewtopic.php?f=3&t=7078#p47432 에서 간략하게 (5 개의 짧은 게시물) 토론했습니다 .

<!-- : 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>

최종 "quit"라인이 없다는 점을 제외하면 출력은 순수한 배치 솔루션과 동일합니다.


4

참고-돌이켜 보면 질문을 다시 읽으면 요청 된 내용이 수행되지 않습니다. 네트워크를 통해 작동하는 흥미로운 방식으로 두 프로세스를 함께 연결하지만 두 방법을 연결하지는 않습니다.


나는 당신이 이것에 약간의 답변을 얻을 수 있기를 바랍니다.

여기 내 대답이 있지만 동의하지 않습니다. 다른 답변을 기다리십시오. 다른 답변을보고 싶습니다.

이것은 cygwin에서 이루어졌습니다. 그리고 'nc'명령 (영리한 명령)을 사용합니다. 'wc -l'은 행만 계산합니다. 그래서 nc를 사용하여 두 가지 명령 (이 경우 echo 및 wc)을 연결합니다.

왼쪽의 명령이 먼저 수행되었습니다.

nc는 a) 서버를 만들거나 b) 원시 모드의 telnet 명령과 같이 서버에 연결할 수있는 명령입니다. 왼쪽 명령에서 'a'사용법을 사용하고 오른쪽 명령에서 'b'사용법을 사용하고 있습니다.

그래서 nc는 입력을 기다리는 곳에 앉아 있었고, 그 입력을 파이프에 파이프 wc -l하고 라인을 세고 입력 된 라인 수를 출력했습니다.

그런 다음 줄을 실행하여 텍스트를 반향하고 해당 원시를 언급 된 서버 인 127.0.0.1:123으로 보냅니다.

여기에 이미지 설명을 입력하십시오

cygwin에서 nc.exe 명령을 복사하여 동일한 디렉토리에 필요한 cygwin1.dll 파일을 사용해보십시오. 또는 내가 가진 것처럼 cygwin 자체에서 할 수 있습니다. gnuwin32에 nc.exe가 표시되지 않습니다. 검색 http://gnuwin32.sourceforge.net/이 있고 nc 또는 netcat이 나타나지 않습니다. 그러나 cygwin을 얻을 수 있습니다 https://cygwin.com/install.html


내가 무슨 일이 일어나고 있는지 이해하기 전에 이것을 읽는 데 10 번이나 걸렸습니다 ..... 그것은 다소 영리합니다
DarthRubik

@DarthRubik 예, netstat -aon | find ":123"왼쪽의 명령으로 서버가 생성되었음을 알 수 있습니다
barlop

그러나 다른 방향 (즉, wc뒤에서 echo명령으로) 을 어떻게 전달합니까?
DarthRubik

nc를 사용하여 두 가지 방법으로 작동시키지 않으면 nc가 일종의 루프에 빠질 수 있습니다.
barlop

nc -lp9999 | prog1 | prog2 | nc 127.0.0.1 9999버퍼가 적시에 플러시되도록 단계를 밟아야 할 수도 있습니다
Jasen

2

한 가지 해킹 (이 작업을 선호하지 않지만 지금은 내가 할 일입니다)은 C # 응용 프로그램을 작성하여 사용자를 위해합니다. 나는이 프로그램에서 몇 가지 주요 기능을 구현하지 않았지만 (실제로 나에게 주어진 인수를 사용하는 것처럼) 여기에 있습니다.

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();

        }
    }
}

그런 다음이 프로그램이 완전히 작동하고 디버그 코드가 제거되면 다음과 같이 사용하십시오.

joiner "A A's args" "B B's Args"
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.