Показать процесс выполнения команды в режиме SSH (Renci SSH.Net)

Помогите найти примеры выполнения команд с динамическим вывыодом в реальном времени в режиме SSH, с помощью Ranci SSH.Net

Есть задача вывести команду top (показывает процессы на сервере в режиме реального времени), но при выполнении кода вида:

ConnectionInfo connectionClient = new ConnectionInfo
    ("HOST", "USER", new PasswordAuthenticationMethod("USER", "PASSWORD"));
SshClient client = new SshClient(connectionClient);
client.Connect();
Console.WriteLine(client.RunCommand("top -b -n 1").Result);

выводится только статический снепшот, а нужно, чтобы информация выводилась постоянно по ходу выполнения команды на сервере.

То же самое касается запуска длинного .sh скрипта, запуск с кода, как выше не покажет процесс его выполнения. Возможно кому-то уже приходилось решать подобное или хотя бы подскажите в какую сторону копать?


Ответы (1 шт):

Автор решения: aepot

Моя фряха не поддерживает подключение с паролем, а только через клаву (keyboard-interactive), поэтому я вхожу на сервак немного иначе.

Чтобы консоль могла пережевывать xterm управляющие коды, ее надо подготовить.

Вспомогательные структуры и Win API

[Flags]
public enum ConsoleOutputMode : uint
{
    EnableProcessedOutput = 0x0001,
    EnableWrapAtEolOutput = 0x0002,
    EnableVirtualTerminalProcessing = 0x0004,
    DisableNewlineAutoReturn = 0x0008,
    EnableLvbGridWorldwide = 0x0010,
}

public enum ConsoleStdHandle : int
{
    StandardInput = -10,
    StandardOutput = -11,
    StandardError = -12
}

internal static class NativeMethods
{
    [DllImport("kernel32.dll")]
    public static extern IntPtr GetStdHandle(ConsoleStdHandle nStdHandle);

    [DllImport("kernel32.dll")]
    public static extern bool GetConsoleMode(IntPtr hConsoleInput, ref int lpMode);

    [DllImport("kernel32.dll")]
    public static extern bool SetConsoleMode(IntPtr hConsoleInput, int dwMode);
}

И вот такой код получился.

class Program
{
    static void Main(string[] args)
    {
        SetupConsoleOutput();

        var authMethod = new KeyboardInteractiveAuthenticationMethod("aepot");
        authMethod.AuthenticationPrompt += AuthMethod_AuthenticationPrompt;
        ConnectionInfo connectionClient = new ConnectionInfo("aepot.ru", "aepot", authMethod);
        using SshClient client = new SshClient(connectionClient);
        client.Connect();
        ReadTop(client);

        Console.WriteLine("Done.");
        Console.ReadKey();
    }

    private static void SetupConsoleOutput()
    {
        Console.OutputEncoding = Encoding.UTF8;
        IntPtr handle = NativeMethods.GetStdHandle(ConsoleStdHandle.StandardOutput);
        int mode = 0;
        NativeMethods.GetConsoleMode(handle, ref mode);
        mode |= (int)ConsoleOutputMode.EnableVirtualTerminalProcessing;
        NativeMethods.SetConsoleMode(handle, mode);
    }

    private static void ReadTop(SshClient client)
    {
        using ShellStream s = client.CreateShellStream("xterm", (uint)Console.WindowWidth, (uint)Console.WindowHeight - 1, 0, 0, Console.WindowWidth * (Console.WindowHeight - 1));
        Console.Clear();
        s.DataReceived += S_DataReceived;
        s.WriteLine("top 1");
        Console.ReadKey(true);
        s.DataReceived -= S_DataReceived;
        s.Write("\x003"); // Ctrl+C
    }

    private static readonly Stream _stdOut = Console.OpenStandardOutput();

    private static void S_DataReceived(object sender, ShellDataEventArgs e)
    {
        _stdOut.Write(e.Data);
    }

    private static void AuthMethod_AuthenticationPrompt(object sender, AuthenticationPromptEventArgs e)
    {
        foreach (AuthenticationPrompt prompt in e.Prompts)
        {
            Console.Write(prompt.Request);
            prompt.Response = ReadPassword(false);
        }
    }

    // чтение пароля с клавиатуры
    private static string ReadPassword(bool displayAsterisk = true)
    {
        StringBuilder sb = new StringBuilder();
        ConsoleKey key;
        do
        {
            ConsoleKeyInfo keyInfo = Console.ReadKey(true);
            key = keyInfo.Key;

            if (key == ConsoleKey.Backspace && sb.Length > 0)
            {
                if (displayAsterisk)
                    Console.Write("\b \b");
                sb.Length--;
            }
            else if (!char.IsControl(keyInfo.KeyChar))
            {
                if (displayAsterisk)
                    Console.Write("*");
                sb.Append(keyInfo.KeyChar);
            }
        } while (key != ConsoleKey.Enter);
        Console.WriteLine();
        return sb.ToString();
    }
}

Данные в консоли обновляются в режиме реального времени

last pid: 35258;  load averages:  0,41,  0,30,  0,27                                           up 139+15:00:15 14:40:24
50 processes:  1 running, 49 sleeping
CPU:  0,2% user,  0,0% nice,  0,2% system,  0,0% interrupt, 99,6% idle
Mem: 39M Active, 1365M Inact, 96M Laundry, 327M Wired, 95M Buf, 136M Free
Swap: 4096M Total, 521M Used, 3575M Free, 12% Inuse

  PID USERNAME    THR PRI NICE   SIZE    RES STATE    C   TIME    WCPU COMMAND
 2732 mysql        47  20    0   935M    37M select   0 416:22   0,22% mysqld

Более того, вывод в консоль у меня поддерживает даже цвета в консоли. То есть Midnight Commander выглядит отлично!

→ Ссылка