StdInとStdOutが関連付けられている2つのプログラム


12

と呼ばれる2つのプログラムがあるとProgramAProgramBます。Windows cmdインタープリターで両方を同時に実行したいと思います。しかし、私はしたいStdOutProgramAにフックをStdInするProgramBStdOutProgramBへのフックStdInProgramA

このようなもの

 ________________ ________________
| | | |
| StdIn(==←===←==(StdOut |
| プログラムA | | プログラムB |
| | | |
| StdOut)==→===→==)StdIn |
| ________________ | | ________________ |

これを行うコマンドはありますか?cmdからこの機能を実現する方法はありますか?


4
UNIXでは、名前付きパイプを使用しますが、これを行うには、ウィンドウには名前付きパイプと呼ばれるものがありますが、これは完全に異なり、おそらく適用されません。
-Jasen

ここでのコメントによると@Jasen linuxjournal.com/article/2156 ので、多分あなたは手の込んだと答えとしてポストすることができれば最初のcygwinの上で価値がテストものの.. cygwinの上で動作する可能性がある名前付きパイプを
barlop

ループしないようにプログラムも書かなければならないと思います。したがって、stdoutが空行の場合はstdinにフィードしないでください。stdinに何もしない場合は終了します。したがって、無限ループを回避しますか?しかし、興味深いことに、アプリケーションは何ですか?
barlop

2
「エリザ」の2つのインスタンスに会話をさせたいと言いますか?
ジェイセン

プログラムがこれを処理するように慎重に設計されていない場合、デッドロックが発生する可能性があります-両方が他からの入力を待っているため、何も起こりません)
Random832

回答:


5

それを行うことができるだけでなく、バ​​ッチファイルだけで行うことができます!:-)

この問題は、一時ファイルを「パイプ」として使用することで解決できます。双方向通信には、2つの「パイプ」ファイルが必要です。

プロセスAは「pipe1」からstdinを読み取り、「pipe2」にstdoutを書き込みます
プロセスBは「pipe2」からstdinを読み取り、「pipe1」にstdoutを書き込みます

どちらかのプロセスを起動する前に、両方のファイルが存在することが重要です。ファイルは最初は空である必要があります。

バッチファイルが現在の末尾にあるファイルから読み取ろうとすると、何も返されず、ファイルは開いたままになります。したがって、私の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に情報を渡すために必要な一時的な「パイプ」ファイルは1つだけです。プロセスは「quit」メッセージをBに送信する必要はありません。Bプロセスは、ファイルの終わりに達するまで単純にループします。

VBSの適切なスリープ機能にアクセスできることは確かに素晴らしいことです。これにより、readLine関数に短い遅延を簡単に導入して、CPUにブレークを与えることができます。

ただし、readLIneには1つのしわがあります。最初は、readLineがstdinに情報があることを検出し、Bが行の書き込みを完了する前にすぐに行を読み取ろうとするまで、断続的にエラーが発生していました。ファイルの終わりのテストと読み取りの間に短い遅延を導入することで問題を解決しました。5ミリ秒の遅延が私にとってはうまくいくように見えましたが、安全のためにそれを10ミリ秒に倍増しました。バッチがこの問題の影響を受けないことは非常に興味深いです。これについては、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>

出力は純粋なバッチソリューションの場合と同じですが、最終的な「終了」行が存在しない点が異なります。


4

注-振り返ってみると、質問をもう一度読んでも、これは求められていることをしません。なぜなら、それは2つのプロセスをリンクしますが(ネットワーク上でも動作する面白い方法で!)、両方のリンクをリンクしません。


これに対するいくつかの答えが得られれば幸いです。

ここに私の答えがありますが、それを受け入れないでください、他の答えを待ってください、私はいくつかの他の答えを見たいです。

これはcygwinから行われました。そして、「nc」コマンド(賢いコマンド)を使用します。'wc -l'は行を数えるだけです。したがって、ncを使用して、この場合はechoとwcの2つのコマンドをリンクしています。

左側のコマンドが最初に実行されました。

ncは、a)サーバーを作成、またはb)telnetコマンドのようにrawモードでサーバーに接続できるコマンドです。左のコマンドで「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

1つのハック(私はこれを行わない方が良いと思いますが、これは今のところ行っていることです)は、これを行うための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.