2009-09-14 9 views
20

Potrzebuję użyć powłoki bash "wewnątrz" programu C#. Chcę naśladować typowanie użytkownika w trybie interaktywnym i uruchamianie poleceń cygwin.Używanie bash (cygwin) wewnątrz programu C#

Utworzono proces, który uruchamia bash i przekierowuje stdin, stout i std error, ale mogę; t get tty do pracy załączony jest przykładowy kod, który uruchamia proces bash i przekierowuje wejście/wyjście.

Problem polega na tym, że nie mam urządzenia tty. Gdy próbuję uruchomić polecenie tty lub polecenia stty i odbierać reakcja na błąd

tty - not a tty 
stty - Inappropriate ioctl for device 

myślę, że jest to spowodowane z psi.UseShellExecute = false;

trzeba uruchomić cygwin i wyłącz echo z stty -echo ale aby to zrobić I potrzebujesz urządzenia tty. jak mogę utworzyć powłokę bash cygwin z urządzeniem tty i przekierować stdin, out i error?

1) czego brakuje?

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Linq; 
using System.Text; 
using System.Threading; 

namespace shartCygwin 
{ 
    class Program 
    { 
     private static Queue<string> ResponseQueue = null; 
     private static ManualResetEvent ResponseEvent = null; 

     static void Main(string[] args) 
     { 
      ResponseQueue = new Queue<string>(); 
      ResponseEvent = new ManualResetEvent(false); 

      Process bashProcess = new Process(); 

      bashProcess.StartInfo.FileName = "C:\\cygwin\\bin\\bash.exe"; 
      bashProcess.StartInfo.Arguments = "--login -i "; 
      bashProcess.StartInfo.WorkingDirectory = "C:\\cygwin\\bin"; 

      bashProcess.StartInfo.EnvironmentVariables["CYGWIN"] = "tty"; 

      bashProcess.StartInfo.RedirectStandardError = true; 
      bashProcess.StartInfo.RedirectStandardInput = true; 
      bashProcess.StartInfo.RedirectStandardOutput = true; 
      bashProcess.StartInfo.CreateNoWindow = true; 
      bashProcess.StartInfo.UseShellExecute = false; 
      bashProcess.StartInfo.ErrorDialog = false; 

      bashProcess.Start(); 

      DataReceivedEventHandler errorEventHandler = new DataReceivedEventHandler(ErrorDataReceived); 
      DataReceivedEventHandler outEventHandler = new DataReceivedEventHandler(OutDataReceived); 
      bashProcess.OutputDataReceived += outEventHandler; 
      bashProcess.ErrorDataReceived += errorEventHandler; 
      bashProcess.BeginErrorReadLine(); 
      bashProcess.BeginOutputReadLine(); 

      while(true) 
      { 
       Thread.Sleep(1000); 
      } 
     } 

     static void ErrorDataReceived(object sender, DataReceivedEventArgs dataReceivedEventArgs) 
     { 
      try 
      { 
       lock (ResponseQueue) 
       { 
        Console.WriteLine(dataReceivedEventArgs.Data); 
        ResponseQueue.Enqueue(dataReceivedEventArgs.Data); 
        ResponseEvent.Set(); 
       } 
      } 
      catch (Exception e) 
      { 
       Console.WriteLine(e.Data); 
      } 
     } 

     static void OutDataReceived(object sender, DataReceivedEventArgs dataReceivedEventArgs) 
     { 
      try 
      { 
       lock (ResponseQueue) 
       { 
        Console.WriteLine(dataReceivedEventArgs.Data); 
        ResponseQueue.Enqueue(dataReceivedEventArgs.Data); 
        ResponseEvent.Set(); 
       } 
      } 
      catch (Exception e) 
      { 
       Console.WriteLine(e.Data); 
      } 
     } 
    } 
} 
+0

Uruchomiłem twój program i mimo, że widzę błąd "Nieodpowiedni ioctl dla urządzenia", widzę po nim komunikat o błędzie cygwin. Na czym dokładnie polega problem? Usunąłem linię CYGWIN = tty i widzę, że te dane wyjściowe są nieprzetworzone, z wyświetlanymi kodami kontrolnymi. –

+0

@Hemlock - Myślę, że kluczem tutaj, opartym na odpowiedzi Kevina Marka, jest to, że decydujesz, czy użyć echa, czy to naprawdę ma znaczenie, jeśli wyłączysz echo/etc ... I jaka jest motywacja za próbę oszukania CYGWINA do używania TTY? – Peter

Odpowiedz

2

marginesie, nie jest prawdziwa odpowiedź, rzucić okiem na: http://www.codeproject.com/KB/IP/sharpssh.aspx

Aby odpowiedzieć na pytanie:

Twój nieprawidłowo obsługi zdarzeń ... Trzeba spojrzeć na e .Data == null w procedurze obsługi zdarzeń dla odebranego błędu/wyjścia. Gdy oba programy obsługi zdarzeń otrzymają to zdarzenie ORAZ proces zakończy się, skończysz. W ten sposób czekasz na trzy uchwyty, jeden, aby poinformować o wywołanym zdarzeniu Process.Exited, po jednym, aby poinformować o otrzymanym wyjściu błędu o wartości null, po jednym, aby poinformować o otrzymanym wyjściu o wartości null. Należy również ustawić:

process.EnableRaisingEvents = true; 

Oto pełna odpowiedź przekierowywanie danych wyjściowych do bieżącego konsoli:

static int RunProgram(string exe, params string[] args) 
    { 
     ManualResetEvent mreProcessExit = new ManualResetEvent(false); 
     ManualResetEvent mreOutputDone = new ManualResetEvent(false); 
     ManualResetEvent mreErrorDone = new ManualResetEvent(false); 

     ProcessStartInfo psi = new ProcessStartInfo(exe, String.Join(" ", args)); 
     psi.WorkingDirectory = Environment.CurrentDirectory; 

     psi.RedirectStandardError = true; 
     psi.RedirectStandardOutput = true; 
     psi.CreateNoWindow = true; 
     psi.UseShellExecute = false; 
     psi.ErrorDialog = true; 

     Process process = new Process(); 
     process.StartInfo = psi; 

     process.Exited += delegate(object o, EventArgs e) 
     { 
      Console.WriteLine("Exited."); 
      mreProcessExit.Set(); 
     }; 
     process.OutputDataReceived += delegate(object o, DataReceivedEventArgs e) 
     { 
      if(e.Data != null) 
       Console.WriteLine("Output: {0}", e.Data); 
      else 
       mreOutputDone.Set(); 
     }; 
     process.ErrorDataReceived += delegate(object o, DataReceivedEventArgs e) 
     { 
      if (e.Data != null) 
       Console.Error.WriteLine("Error: {0}", e.Data); 
      else 
       mreErrorDone.Set(); 
     }; 

     process.EnableRaisingEvents = true; 
     Console.WriteLine("Start: {0}", process.StartInfo.FileName); 
     process.Start(); 
     process.BeginErrorReadLine(); 
     process.BeginOutputReadLine(); 

     if (process.HasExited) 
      mreProcessExit.Set(); 

     while(!WaitHandle.WaitAll(new WaitHandle[] { mreErrorDone, mreOutputDone, mreProcessExit }, 100)) 
      continue; 
     return process.ExitCode; 
    } 
+0

Problem polega na tym, że nie mam urządzenia tty. Jeśli spróbuję uruchomić polecenie tty lub polecenie stty, otrzymuję odpowiedź o błędzie tty - nie jest to tty stty - Nieodpowiednie urządzenie ioctl dla urządzenia Myślę, że jest to spowodowane przez psi.UseShellExecute = false; Muszę uruchomić cygwin i wyłączyć echo z stty -echo, ale w tym celu potrzebuję urządzenia tty. w jaki sposób utworzę powłokę bash cygwin z urządzeniem tty i przekierowuję stdin, out i error? –

+0

Twój problem polega na tym, że stty -echo próbuje ustawić ustawienia terminala, ale kiedy przekierujesz standardowe wyjście do innego potoku, polecenie to generuje błąd. Kod przekierowania przez csharptest.net działa dla ciebie, ale przekierowanie i ustawienia związane z konsolą zabraniają się nawzajem. Powinieneś zmienić wywoływany skrypt, aby wykryć przypadek przekierowania wyjścia, np. dzwoniąc do stty -echo, aby sprawdzić, czy jesteś przekierowany, czy nie. –

6

To może lub nie może cię ani nikogo innego, kto się dzieje po drugiej stronie tej kwestii pomóc. To jest odpowiedź na to samo dokładne pytanie na liście mailingowej Cygwin.

> i created a process that runs bash and redirect stdin,stout and std error 
> but I can’t get tty to work attached is a sample code that starts bash 
> process and redirect the input/output. 
> the problem is that i don't have tty device. if i try to run tty command or 
> stty command i receive error response 
> tty - not a tty 
> stty - Inappropriate ioctl for device 

> i need to run cygwin and disable echo with stty -echo but to do this i need 
> a tty device. how can i create a cygwin bash shell with tty device and 
> redirect the stdin, out and error ? 

    Why exactly do you think you need to run stty and set the tty operating 
parameters, when the bash process is quite plainly *not* connected to a tty, 
it is connected to your C# application? 

    It's your application that is in charge of I/O - if it doesn't want echo, 
all it has to do is discard the stuff it reads from the process' stdout 
instead of displaying it, in your OutDataReceived/ErrorDataReceived handlers. 

>    bashProcess.StartInfo.EnvironmentVariables["CYGWIN"] = "tty"; 

    Don't do this. Win32 native processes don't understand Cygwin's tty 
emulation, which is based on pipes. 

    cheers, 
     DaveK

Źródło: http://www.cygwin.com/ml/cygwin/2009-09/msg00637.html

0

Wystarczy Exec coś takiego:

C:\cygwin\bin\bash -li /cygdrive/c/<path-to-shell-script-location>/chmod-cmd.sh 

A potem zatrzask na wejściu i wyjściu.

LUB użyj mintty.