在 C# 程序中使用 bash(cygwin)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1423484/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-17 21:12:04  来源:igfitidea点击:

Using bash(cygwin) inside C# program

c#bashprocesscygwin

提问by

i need to use bash shell "inside" C# program. I want to mimic user typing in interactive mode and running cygwin commands.

我需要在“内部”C# 程序中使用 bash shell。我想模仿用户在交互模式下输入并运行 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.

我创建了一个运行 bash 并重定向 stdin、stout 和 std 错误的进程,但我不能;无法让 tty 工作附加是一个示例代码,它启动 bash 进程并重定向输入/输出。

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 设备。如果我尝试运行 tty 命令或 stty 命令,我会收到错误响应

tty - not a tty 
stty - Inappropriate ioctl for device

i think the this is caused from psi.UseShellExecute = false;

我认为这是由 psi.UseShellExecute = false;

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

我需要运行 cygwin 并使用 stty -echo 禁用 echo,但要做到这一点,我需要一个 tty 设备。我如何使用 tty 设备创建一个 cygwin bash shell 并重定向标准输入、输出和错误?

1) what am missing ?

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

回答by Kevin Mark

This may or may not help you or anybody else who happens across this question. This is the answer given to the same exact question on the Cygwin mailing list.

这可能会也可能不会帮助您或遇到此问题的任何其他人。这是对 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

Source: http://www.cygwin.com/ml/cygwin/2009-09/msg00637.html

来源:http: //www.cygwin.com/ml/cygwin/2009-09/msg00637.html

回答by csharptest.net

A side note, not a real answer, have a look at: http://www.codeproject.com/KB/IP/sharpssh.aspx

旁注,不是真正的答案,看看:http: //www.codeproject.com/KB/IP/sharpssh.aspx

To answer the question:

要回答这个问题:

Your not correctly handling the events... You need to look for e.Data == null in the event handler for Error/Output received. Once both event handlers receive this event AND the process has terminated you are done. Thus you wait on three handles, one to tell you the Process.Exited event fired, one to tell you the error output received null, one to tell you the output received null. Be sure to also set:

您没有正确处理事件...您需要在接收到的错误/输出的事件处理程序中查找 e.Data == null。一旦两个事件处理程序都收到此事件并且进程终止,您就完成了。因此,您等待三个句柄,一个告诉您 Process.Exited 事件已触发,一个告诉您错误输出接收到空值,一个告诉您输出接收到空值。一定还要设置:

process.EnableRaisingEvents = true;

Here is the full answer redirecting output to current console:

这是将输出重定向到当前控制台的完整答案:

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

回答by Ben Poulson

Just exec something like:

只是执行类似:

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

And then latch onto the input and output.

然后锁存输入和输出。

OR use mintty.

或使用mintty