Использование bash(cygwin) внутри программы C#

мне нужно использовать оболочку bash "внутри" программы С#. Я хочу имитировать ввод пользователем в интерактивном режиме и выполнение команд cygwin.

я создал процесс, который запускает bash и перенаправляет ошибки stdin, stout и std, но я не могу заставить работать tty. Прилагается пример кода, который запускает процесс bash и перенаправляет ввод/вывод.

проблема в том, что у меня нет tty устройства. если я пытаюсь запустить команду tty или команду stty, я получаю ответ об ошибке

tty - not a tty 
stty - Inappropriate ioctl for device

я думаю, что это вызвано psi.UseShellExecute = false;

мне нужно запустить cygwin и отключить эхо с помощью stty -echo, но для этого мне нужно устройство tty. как я могу создать оболочку cygwin bash с устройством tty и перенаправить стандартный ввод, вывод и ошибку?

1) чего не хватает?

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

person Community    schedule 14.09.2009    source источник
comment
Я запустил вашу программу, и хотя я вижу ошибку «Несоответствующий ioctl для устройства», после этого я также вижу подсказку cygwin bash. В чем именно проблема? Я удалил строку CYGWIN=tty и вижу, что вывод является необработанным, с отображаемыми контрольными кодами.   -  person Nikhil Dabas    schedule 07.06.2012
comment
@Hemlock - я думаю, что ключ здесь, основываясь на ответе Кевина Марка, заключается в том, что вы можете решить, использовать ли эхо или нет, так что действительно ли имеет значение, отключите ли вы эхо / и т. д. ... И какая мотивация для этого? пытаетесь заставить CYGWIN использовать TTY?   -  person Peter    schedule 07.06.2012


Ответы (3)


Это может помочь или не помочь вам или кому-либо еще, кто сталкивается с этим вопросом. Это ответ на тот же самый вопрос в списке рассылки 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

Источник: http://www.cygwin.com/ml/cygwin/2009-09/msg00637.html

person Kevin Mark    schedule 26.10.2010

Дополнительное примечание, а не настоящий ответ, взгляните на: http://www.codeproject.com/KB/IP/sharpssh.aspx

Чтобы ответить на вопрос:

Вы неправильно обрабатываете события... Вам нужно искать e.Data == null в обработчике событий для полученной ошибки/вывода. Как только оба обработчика событий получат это событие И процесс завершится, вы закончите. Таким образом, вы ждете три дескриптора: один, чтобы сообщить вам, что событие Process.Exited запущено, один, чтобы сообщить вам, что вывод ошибки получил null, и один, чтобы сообщить вам, что вывод получил null. Не забудьте также установить:

process.EnableRaisingEvents = true;

Вот полный ответ, перенаправляющий вывод на текущую консоль:

    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;
    }
person csharptest.net    schedule 14.09.2009
comment
проблема в том, что у меня нет tty устройства. если я пытаюсь запустить команду tty или команду stty, я получаю ответ об ошибке tty - не tty stty - неподходящий ioctl для устройства, я думаю, что это вызвано psi.UseShellExecute = false; мне нужно запустить cygwin и отключить эхо с помощью stty -echo, но для этого мне нужно устройство tty. как я могу создать оболочку cygwin bash с устройством tty и перенаправить стандартный ввод, вывод и ошибку? - person ; 15.09.2009
comment
Ваша проблема в том, что stty -echo пытается установить настройки на терминале, но когда вы перенаправляете стандартный вывод на другой канал, эта команда выдает ошибку. Код перенаправления от csharptest.net работает для вас, но перенаправление и настройка консоли запрещают друг друга. Вы должны изменить вызываемый скрипт, чтобы обнаружить случай, когда вывод перенаправляется, например. просто позвонив stty -echo, чтобы проверить, перенаправлены вы или нет. - person Alois Kraus; 04.06.2012

Просто выполните что-то вроде:

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

А затем защелкнуть на входе и выходе.

ИЛИ используйте mintty.

person Ben Poulson    schedule 08.06.2012