StdIn ve StdOut'ları bağlı iki program


12

Diyelim ki ProgramAve adında iki programım var ProgramB. Her ikisini aynı anda Windows cmd yorumlayıcısında çalıştırmak istiyorum. Ama istediğim StdOutbir ProgramAçengel StdInarasında ProgramBve StdOutiçinde ProgramBçengel StdInarasında ProgramA.

Böyle bir şey

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

Bunu yapmak için herhangi bir komut var mı - cmd'den bu işlevselliğe ulaşmanın bir yolu var mı?


4
Unix'te adlandırılmış borular kullanacağım, bunu yapmak için pencerelerde tamamen farklı olan ve muhtemelen uygulanamayan adlandırılmış borular adı verilen bir şey var.
Jasen

@Jasen burada bir yoruma göre linuxjournal.com/article/2156 adlı borular cygwin üzerinde çalışabilir .. öyleyse, belki o zaman belki cygwin üzerinde test etmeye değer olsa da, bir ayrıntı olarak detaylandırıp gönderebilirsiniz
barlop

Ben de programlar döngü olmamak için de yazılması gerektiğini varsayalım .. Yani stdout boş bir çizgi ise o zaman stdin beslemek yok, ve eğer hiçbir şey stdin o zaman çıkmak. Böylece sonsuz bir döngüden kaçınmak? Ama ilgisiz, uygulama nedir?
barlop

2
"Eliza" nın iki örneğini konuşmak istediğinizi mi söylüyorsunuz?
Jasen

Programlar bununla başa çıkmak için dikkatle tasarlanmamışsa bir kilitlenmeye uğrayabilirler - her ikisi de diğerinden girdi bekler ve hiçbir şey olmaz (ya da her ikisi de tam arabelleklere yazmaya çalışıyor ve bunları boşaltmak için hiçbir şey okumuyorlar) )
Random832

Yanıtlar:


5

Sadece bir toplu iş dosyası dışında hiçbir şey ile yapılabilir! :-)

Sorun geçici bir dosya "yöneltme" olarak kullanılarak çözülebilir. Çift yönlü iletişim için iki "kanal" dosyası gerekir.

İşlem A stdin'i "pipe1"
den okur ve stdout'u "pipe2" ye yazar. İşlem B stdin'i "pipe2" den okur ve stdout'u "pipe1" e yazar.

Her iki dosyanın da her iki işlemi başlatmadan önce bulunması önemlidir. Dosyalar başlangıçta boş olmalıdır.

Toplu iş dosyası geçerli uçta olan bir dosyadan okumaya çalışırsa, hiçbir şey döndürmez ve dosya açık kalır. Bu nedenle, readLine yordamım, boş olmayan bir değer alana kadar sürekli olarak okur.

Boş bir dize okumak ve yazmak istiyorum, bu yüzden benim writeLine rutin readLine şeritler ek bir karakter ekler.

A sürecim akışı kontrol eder. 1 (B'ye mesaj) yazarak işleri başlatır ve 10 değerini yineleyen bir döngüye girer (burada bir değer okur (B'den mesaj), 1 ekler ve sonra sonucu yazar (B'ye mesaj). Son olarak B'den son mesajı bekler ve sonra B'ye bir "quit" mesajı yazar ve çıkar.

B sürecim bir değeri (A'dan gelen mesaj) okuyan, 10 ekleyen ve sonra sonucu (A'ya mesaj) yazan koşullu sonsuz bir döngüdedir. B bir "quit" mesajı okursa, derhal sona erer.

İletişimin tamamen eşzamanlı olduğunu göstermek istedim, bu yüzden hem A hem de B işlem döngülerinde bir gecikme getirdim.

ReadLine yordamının, girişi beklerken hem CPU'yu hem de dosya sistemini sürekli olarak kötüye kullanan sıkı bir döngüde olduğunu unutmayın. Döngüye bir PING gecikmesi eklenebilir, ancak daha sonra işlemler o kadar duyarlı olmayacaktır.

Hem A hem de B işlemlerini başlatmak için gerçek bir boru kullanıyorum. Ancak boru, hiçbir iletişimin geçmemesi nedeniyle işlevsel değildir. Tüm iletişim geçici "kanal" dosyaları üzerinden yapılır.

İşlemleri başlatmak için START / B'yi de kullanabilirdim, ancak daha sonra her ikisinin de ne zaman sonlanacağını tespit etmeliyim ki geçici "boru" dosyalarını ne zaman sileceğimi biliyorum. Boruyu kullanmak çok daha kolaydır.

Tüm kodu tek bir dosyaya koymayı seçtim - A ve B'yi başlatan ana komut dosyası ve A ve B kodu. Her işlem için ayrı bir komut dosyası kullanmış olabilirim.

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

--ÇIKTI--

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

Üst düzey bir dil ile yaşam biraz daha kolaydır. A ve B işlemleri için VBScript kullanan bir örnek aşağıdadır. İşlemleri başlatmak için hala toplu işi kullanıyorum. Ben çok serin yöntem de tarif kullanmak gömmek mümkün o ve geçici bir dosya kullanmadan bir toplu dosya içinde VBScript'i yürütmek? birden çok VBS komut dosyasını tek bir toplu iş komut dosyasına gömmek için.

VBS gibi daha yüksek bir dilde, A'dan B'ye bilgi iletmek için normal bir boru kullanabiliriz. B'den A'ya bilgi aktarmak için yalnızca tek bir geçici "boru" dosyasına ihtiyacımız var. işleminin B'ye bir "quit" iletisi göndermesi gerekmez. B işlemi dosya sonuna ulaşana kadar döngü yapar.

VBS uygun bir uyku fonksiyonuna erişim olması güzel. Bu, CPU'ya bir mola vermek için readLine işlevinde kolayca kısa bir gecikme yapmamı sağlar.

Bununla birlikte, readLIne içinde bir kırışıklık vardır. İlk başta, bazen readLine'ın stdin'de mevcut bilgileri algılayacağını ve B'nin satırı yazmayı bitirme şansı bulamadan hemen önce satırı okumaya çalışacağını anlayana kadar aralıklı hatalar alıyordum. Dosya sonu testi ile okuma arasında kısa bir gecikme getirerek sorunu çözdüm. 5 ms gecikme benim için hile gibi görünüyordu, ama ben sadece güvenli tarafında olmak için 10 ms için iki katına çıktı. Toplu işin bu sorunu yaşamaması çok ilginç. Bu konuyu kısaca tartıştık (5 kısa mesaj) 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>

Çıktı, saf "çözelti" ile aynıdır, ancak son "quit" hatları yoktur.


4

Not- Geriye dönüp baktığımızda, soruyu tekrar okumak, bu istenen şeyi yapmaz. İki süreci birbirine bağlasa da (bir ağ üzerinde bile çalışacak ilginç bir şekilde!), Her iki yolu da bağlamaz.


Umarım buna birkaç cevap alırsınız.

İşte cevabım ama kabul etme, diğer cevapları bekle, başka cevaplar görmeye meraklıyım.

Bu cygwin'den yapıldı. Ve 'nc' komutunu kullanarak (akıllıca). 'Wc -l' sadece satırları sayar. Bu yüzden nc kullanarak her iki komutu, bu durumda echo ve wc'yi bağlıyorum.

Önce soldaki komut yapıldı.

nc, a) sunucu oluşturabilen veya b) ham modda telnet komutu gibi bir sunucuya bağlanabilen bir komuttur. Sol komutta 'a' kullanımını ve sağ komutta 'b' kullanımını kullanıyorum.

Böylece nc orada bir girişi beklerken dinledi ve daha sonra wc -lbu satırları girip hatlara saydı ve girilen satır sayısını çıkardı.

Sonra bazı metin yankı ve bu ham sunucu belirtilen 127.0.0.1:123 göndermek için koştu.

resim açıklamasını buraya girin

Nc.exe komutunu cygwin'den kopyalayabilir ve aynı dizinde gereken cygwin1.dll dosyasını kullanmayı deneyebilirsiniz. Ya da benim gibi cygwin'in kendisinden yapabilirsin. Gnuwin32 içinde nc.exe görmüyorum. Onlar bir arama var http://gnuwin32.sourceforge.net/ kadar gelmiyor ve nc veya netcat'in. Ancak cygwin'i https://cygwin.com/install.html alabilirsiniz


Neler olup bittiğini
anlamadan

@DarthRubik evet ve netstat -aon | find ":123"soldaki komutun sunucuyu oluşturduğunu görmek için kullanabilirsiniz
barlop

Ancak diğer yönü nasıl iletirsiniz (yani wcarkadan echokomuta).
DarthRubik

nc ile her iki şekilde yapmaya çalıştığımda çalışamıyorum, belki nc bir tür döngüye girer.
barlop

nc -lp9999 | prog1 | prog2 | nc 127.0.0.1 9999tamponların zamanında temizlendiğinden emin olmak için adımlar atılması gerekebilir
Jasen

2

Bir kesmek (Bunu yapmamayı tercih ederdim, ama şimdilik bununla devam ediyorum) sizin için bunu yapmak için bir C # uygulaması yazmaktır . Bu programda bazı temel özellikleri uygulamadım (aslında bana verilen argümanları kullanmak gibi), ama işte burada:

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

        }
    }
}

Sonunda, bu program tamamen işlevsel olduğunda ve hata ayıklama kodu kaldırıldığında şöyle bir şey kullanırsınız:

joiner "A A's args" "B B's Args"
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.