Execute long time command in SSH.NET and display the results continuously in TextBox

AAHN picture AAHN · Nov 20, 2017 · Viewed 9.7k times · Source

Is there any way to execute Linux command and display the result in text box in Windows application like PuTTY.

For example I'm trying to execute the following commands

wget http://centos-webpanel.com/cwp-latest
sh cwp-latest

using the following code

SshClient sshclient = new SshClient(IPtxtBox.Text, UserNameTxt.Text, PasswordTxt.Text);
sshclient.Connect();
ShellStream stream = sshclient.CreateShellStream("customCommand", 80, 24, 800, 600, 1024);

resultTxt.Text = SSHCommand.SendCommand(stream, "wget http://centos-webpanel.com/cwp-latest && sh cwp-latest");
private static void WriteStream(string cmd, StreamWriter writer, ShellStream stream)
{
    writer.WriteLine(cmd);
    while (stream.Length == 0)
        Thread.Sleep(500);
}
private static string ReadStream(StreamReader reader)
{
    StringBuilder result = new StringBuilder();

    string line;
    while ((line = reader.ReadLine()) != null)
        result.AppendLine(line);

    return result.ToString();
}
private static string SendCommand(ShellStream stream, string customCMD)
{
    StringBuilder strAnswer = new StringBuilder();

    var reader = new StreamReader(stream);
    var writer = new StreamWriter(stream);
    writer.AutoFlush = true;
    WriteStream(customCMD, writer, stream);

    strAnswer.AppendLine(ReadStream(reader));

    string answer = strAnswer.ToString();
    return answer.Trim();
}

This command takes along time to be executed and no result appeared on the result text box.

Answer

Martin Prikryl picture Martin Prikryl · Nov 20, 2017

First, do not use "shell" channel to automate a command execution, unless you have a good reason. Use "exec" channel (CreateCommand or RunCommand in SSH.NET).

To feed the output to a TextBox, just keep reading the stream on a background thread:

private void button1_Click(object sender, EventArgs e)
{
    new Task(() => RunCommand()).Start();
}

private void RunCommand()
{
    var host = "hostname";
    var username = "username";
    var password = "password";

    using (var client = new SshClient(host, username, password))
    {
        client.Connect();
        // If the command2 depend on an environment modified by command1,
        // execute them like this.
        // If not, use separate CreateCommand calls.
        var cmd = client.CreateCommand("command1; command2");

        var result = cmd.BeginExecute();

        using (var reader =
                   new StreamReader(cmd.OutputStream, Encoding.UTF8, true, 1024, true))
        {
            while (!result.IsCompleted || !reader.EndOfStream)
            {
                string line = reader.ReadLine();
                if (line != null)
                {
                    textBox1.Invoke(
                        (MethodInvoker)(() =>
                            textBox1.AppendText(line + Environment.NewLine)));
                }
            }
        }

        cmd.EndExecute(result);
    }
}

For a slightly different approach, see a similar WPF question:
SSH.NET real-time command output monitoring.

Some programs, like Python, may buffer the output, when executed this way. See:
How to continuously write output from Python Program running on a remote host (Raspberry Pi) executed with C# SSH.NET on local console?