ProgramA
and 라고하는 두 개의 프로그램이 있다고 가정합니다 ProgramB
. Windows cmd 인터프리터에서 동시에 두 가지를 모두 실행하고 싶습니다. 그러나 내가 원하는 StdOut
의 ProgramA
받는 후크를 StdIn
의 ProgramB
와 StdOut
의 ProgramB
받는 후크 StdIn
의 ProgramA
.
이 같은
________________ ________________ | | | | | StdIn (== ← === ← == (StdOut | | 프로그램 A | | 프로그램 B | | | | | | StdOut) == → === → ==) StdIn | | ________________ | | ________________ |
이것을 수행하는 명령이 있습니까-cmd 에서이 기능을 달성하는 방법은 무엇입니까?
답변
그것은 할 수있을뿐만 아니라 배치 파일만으로 할 수 있습니다! 🙂
임시 파일을 “파이프”로 사용하면이 문제를 해결할 수 있습니다. 양방향 통신에는 두 개의 “파이프”파일이 필요합니다.
프로세스 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”라인이 없다는 점을 제외하면 출력은 순수한 배치 솔루션과 동일합니다.
답변
참고-돌이켜 보면 질문을 다시 읽으면 요청 된 내용이 수행되지 않습니다. 네트워크를 통해 작동하는 흥미로운 방식으로 두 프로세스를 함께 연결하지만 두 방법을 연결하지는 않습니다.
나는 당신이 이것에 약간의 답변을 얻을 수 있기를 바랍니다.
여기 내 대답이 있지만 동의하지 않습니다. 다른 답변을 기다리십시오. 다른 답변을보고 싶습니다.
이것은 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
답변
한 가지 해킹 (이 작업을 선호하지 않지만 지금은 내가 할 일입니다)은 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"