Dua Program memiliki StdIn dan StdOut mereka diikat

12

Misalkan saya punya dua program yang disebut ProgramAdan ProgramB. Saya ingin menjalankan keduanya secara bersamaan di Windows cmd interpreter. Tapi saya ingin StdOutdari ProgramAdoyan ke StdIndari ProgramBdan StdOutdari ProgramBdoyan ke StdIndari ProgramA.

Sesuatu seperti ini

 ________________ ________________
| | | |
| StdIn (== ← === ← == (StdOut |
| Program A | | Program B |
| | | |
| StdOut) == → === → ==) StdIn |
| ________________ | | ________________ |

Apakah ada perintah untuk melakukan ini - beberapa cara untuk mencapai fungsi ini dari cmd?

DarthRubik
sumber
4
Pada unix saya menggunakan pipa bernama, untuk melakukan ini, windows memiliki sesuatu yang disebut pipa bernama yang sama sekali berbeda dan mungkin tidak berlaku.
Jasen
@Jasen menurut komentar di sini linuxjournal.com/article/2156 bernama pipes mungkin bekerja di cygwin .. jika demikian maka mungkin Anda bisa menguraikan dan memposting sebagai jawaban, meskipun layak diuji pada cygwin pertama
barlop
Saya kira program-program juga harus ditulis agar tidak berulang .. Jadi jika stdout adalah baris kosong maka jangan memberi makan ke stdin, dan jika tidak ada yang stdin maka keluar. Jadi menghindari infinite loop? Tetapi karena ketertarikan, apa aplikasinya?
barlop
2
katakanlah Anda ingin memiliki dua contoh "Eliza" berbincang?
Jasen
Jika program tidak dirancang dengan hati-hati untuk menangani ini, mereka mungkin akan menemui jalan buntu - keduanya menunggu masukan dari yang lain dan tidak ada yang terjadi (atau keduanya mencoba menulis ke buffer penuh dan tidak pernah membaca apa pun untuk mengosongkannya) )
Random832

Jawaban:

5

Tidak hanya itu bisa dilakukan, itu bisa dilakukan dengan apa-apa selain file batch! :-)

Masalahnya dapat diselesaikan dengan menggunakan file sementara sebagai "pipa". Komunikasi dua arah memerlukan dua file "pipa".

Proses A membaca stdin dari "pipe1" dan menulis stdout ke "pipe2"
Proses B membaca stdin dari "pipe2" dan menulis stdout ke "pipe1"

Penting bahwa kedua file ada sebelum meluncurkan proses mana pun. File harus kosong di awal.

Jika file batch mencoba membaca dari file yang kebetulan berada di akhir saat ini, itu hanya mengembalikan apa-apa, dan file tetap terbuka. Jadi rutin readLine saya terus membaca hingga mendapat nilai yang tidak kosong.

Saya ingin dapat membaca dan menulis string kosong, jadi rutinitas writeLine saya menambahkan karakter tambahan yang strip readLine lepas.

Proses A saya mengontrol aliran. Ini memulai sesuatu dengan menulis 1 (pesan ke B), dan kemudian memasuki satu lingkaran dengan 10 iterasi di mana ia membaca nilai (pesan dari B), menambahkan 1, dan kemudian menulis hasilnya (pesan ke B). Akhirnya ia menunggu pesan terakhir dari B, dan kemudian menulis pesan "keluar" ke B dan keluar.

Proses B saya berada dalam loop tanpa syarat bersyarat yang membaca nilai (pesan dari A), menambahkan 10, dan kemudian menulis hasilnya (pesan ke A). Jika B pernah membaca pesan "keluar", maka itu segera berakhir.

Saya ingin menunjukkan bahwa komunikasi sepenuhnya sinkron, jadi saya memperkenalkan penundaan pada loop proses A dan B.

Perhatikan bahwa prosedur readLine dalam loop ketat yang terus-menerus menyalahgunakan CPU dan sistem file sambil menunggu input. Penundaan PING dapat ditambahkan ke loop, tetapi kemudian proses tidak akan responsif.

Saya menggunakan pipa sejati sebagai kemudahan untuk meluncurkan proses A dan B. Tetapi pipa itu tidak berfungsi karena tidak ada komunikasi yang melewatinya. Semua komunikasi adalah melalui file "pipa" sementara saya.

Saya bisa saja menggunakan START / B untuk meluncurkan proses, tetapi kemudian saya harus mendeteksi kapan keduanya berakhir sehingga saya tahu kapan harus menghapus file "pipa" sementara. Jauh lebih mudah menggunakan pipa.

Saya memilih untuk meletakkan semua kode dalam satu file - skrip master yang meluncurkan A dan B, serta kode untuk A dan B. Saya bisa menggunakan file skrip terpisah untuk setiap proses.

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

--KELUARAN--

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

Hidup sedikit lebih mudah dengan bahasa tingkat yang lebih tinggi. Di bawah ini adalah contoh yang menggunakan VBScript untuk proses A dan B. Saya masih menggunakan batch untuk meluncurkan proses. Saya menggunakan metode yang sangat keren yang dijelaskan di Mungkinkah menanamkan dan mengeksekusi VBScript dalam file batch tanpa menggunakan file sementara? untuk menanamkan beberapa skrip VBS dalam skrip batch tunggal.

Dengan bahasa yang lebih tinggi seperti VBS, kita dapat menggunakan pipa normal untuk meneruskan info dari A ke B. Kita hanya perlu satu file "pipa" sementara untuk meneruskan info dari B kembali ke A. Karena kita sekarang memiliki pipa yang berfungsi, A proses tidak perlu mengirim pesan "berhenti" ke B. Proses B hanya loop sampai mencapai akhir file.

Memang menyenangkan memiliki akses ke fungsi tidur yang tepat di VBS. Ini memungkinkan saya untuk dengan mudah memperkenalkan penundaan singkat pada fungsi readLine untuk memberikan CPU istirahat.

Namun, ada satu kerutan di dalam readLIne. Pada awalnya saya mendapatkan kegagalan yang terputus-putus sampai saya menyadari bahwa kadang-kadang readLine akan mendeteksi informasi tersedia di stdin, dan akan segera mencoba membaca baris sebelum B memiliki kesempatan untuk menyelesaikan penulisan baris. Saya memecahkan masalah dengan memperkenalkan penundaan singkat antara tes akhir file dan membaca. Penundaan 5 msec tampaknya melakukan trik untuk saya, tetapi saya menggandakannya menjadi 10 msec hanya untuk berada di sisi yang aman. Sangat menarik bahwa batch tidak mengalami masalah ini. Kami membahas ini secara singkat (5 pos pendek) di 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>

Outputnya sama dengan solusi batch murni, kecuali baris "berhenti" akhir tidak ada.

dbenham
sumber
4

Catatan- Dalam retrospeksi, membaca pertanyaan lagi, ini tidak melakukan apa yang diminta. Karena, sementara itu menghubungkan dua proses bersama-sama, (dengan cara yang menarik yang bahkan akan bekerja melalui jaringan!), Itu tidak menghubungkan dua arah.


Saya harap Anda mendapatkan beberapa jawaban untuk ini.

Inilah jawaban saya tetapi jangan menerimanya, tunggu jawaban lain, saya ingin sekali melihat beberapa jawaban lain.

Ini dilakukan dari cygwin. Dan menggunakan perintah 'nc' (yang pintar). 'Wc-l' hanya menghitung garis. Jadi saya menghubungkan apa pun dua perintah, dalam hal ini, gema dan wc, menggunakan nc.

Perintah di sebelah kiri dilakukan terlebih dahulu.

nc adalah perintah yang dapat a) membuat server, atau b) menghubungkan seperti perintah telnet dalam mode mentah, ke server. Saya menggunakan penggunaan 'a' di perintah kiri, dan penggunaan 'b' di perintah kanan.

Jadi nc duduk di sana mendengarkan menunggu input, dan kemudian akan mengirim input itu ke wc -ldan menghitung garis dan output jumlah baris yang dimasukkan.

Saya kemudian menjalankan baris untuk mengulang beberapa teks dan mengirim mentah ke 127.0.0.1:123 yang merupakan server yang disebutkan.

masukkan deskripsi gambar di sini

Anda bisa menyalin perintah nc.exe dari cygwin dan coba gunakan itu dan file cygwin1.dll yang diperlukan di direktori yang sama. Atau Anda bisa melakukannya dari cygwin sendiri seperti saya. Saya tidak melihat nc.exe di gnuwin32. Mereka memiliki pencarian http://gnuwin32.sourceforge.net/ dan nc atau netcat tidak muncul. Tetapi Anda bisa mendapatkan cygwin https://cygwin.com/install.html

barlop
sumber
Saya butuh sepuluh kali untuk membaca ini sebelum saya mengerti apa yang sedang terjadi ..... itu agak pintar
DarthRubik
@DarthRubik ya, dan Anda dapat menggunakan netstat -aon | find ":123"untuk melihat bahwa perintah di sebelah kiri menciptakan server
barlop
Tetapi bagaimana Anda berkomunikasi ke arah lain (mis. Dari wcbelakang ke echoperintah).
DarthRubik
ketika saya mencoba dengan nc untuk melakukannya dengan dua cara saya tidak bisa membuatnya bekerja, mungkin nc masuk ke dalam semacam lingkaran.
barlop
nc -lp9999 | prog1 | prog2 | nc 127.0.0.1 9999langkah-langkah mungkin perlu diambil untuk memastikan buffer memerah tepat waktu
Jasen
2

Satu peretasan (saya lebih suka tidak melakukan ini, tapi ini yang akan saya lakukan sekarang) adalah menulis aplikasi C # untuk melakukan ini untuk Anda. Saya belum menerapkan beberapa fitur utama dalam program ini (seperti benar-benar menggunakan argumen yang diberikan kepada saya), tapi ini dia:

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

        }
    }
}

Kemudian pada akhirnya, ketika program ini berfungsi penuh dan kode debug dihapus Anda akan menggunakannya seperti ini:

joiner "A A's args" "B B's Args"
DarthRubik
sumber