wpf SSH.NET实时命令输出监控

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31587924/
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-13 13:17:53  来源:igfitidea点击:

SSH.NET real-time command output monitoring

wpfsshasync-awaitmonitoringssh.net

提问by Pixar

There is a long running script script.shon a remote Linux machine. I need to start it and monitor it's activity in real time. The script during it's activity may output to stdoutand stderr. I am searching for a way to capture both of the streams.

script.sh远程 Linux 机器上有一个长时间运行的脚本。我需要启动它并实时监控它的活动。活动期间的脚本可能会输出到stdoutstderr。我正在寻找一种方法来捕获这两个流。

I use Renci SSH.NET to upload script.shand start it, so it would be great to see a solution bounded to this library. In my mind the perfect solution is the new method:

我使用 Renci SSH.NET 上传script.sh并启动它,因此很高兴看到与此库相关的解决方案。在我看来,完美的解决方案是新方法:

var realTimeScreen= ...;

var commandExecutionStatus = sshClient.RunCommandAsync(
    command: './script.sh',
    stdoutEventHandler: stdoutString => realTimeScreen.UpdateStdout(stdString)
    stderrEventHandler: stderrString => realTimeScreen.UpdateStderr(stderrString));
...
commandExecutionStatus.ContinueWith(monitoringTask =>
{
    if (monitoringTask.Completed)
    {
        realTimeScreen.Finish();
    }
});

回答by Martin Prikryl

Use SshClient.CreateCommandmethod. It returns SshCommandinstance.

使用SshClient.CreateCommand方法。它返回SshCommand实例。

The SshCommandclasshas ExtendedOutputStreampropertythat returns a stream with both stdout and stderr.

SshCommand具有ExtendedOutputStream属性,返回与两个输出和错误流。

See SshCommandTest.cs:

SshCommandTest.cs

public void Test_Execute_ExtendedOutputStream()
{
    var host = Resources.HOST;
    var username = Resources.USERNAME;
    var password = Resources.PASSWORD;

    using (var client = new SshClient(host, username, password))
    {
        #region Example SshCommand CreateCommand Execute ExtendedOutputStream

        client.Connect();
        var cmd = client.CreateCommand("echo 12345; echo 654321 >&2");
        var result = cmd.Execute();

        Console.Write(result);

        var reader = new StreamReader(cmd.ExtendedOutputStream);
        Console.WriteLine("DEBUG:");
        Console.Write(reader.ReadToEnd());

        client.Disconnect();

        #endregion

        Assert.Inconclusive();
    }
}


See also a full code for similar WinForms question Execute long time command in SSH.NET and display the results continuously in TextBox.

另请参阅类似 WinForms 问题的完整代码在 SSH.NET 中执行长时间命令并在 TextBox 中连续显示结果

回答by Pixar

So, here is the solution I came up with. Of course, it can be improved, so it is open to critique.
I used

所以,这是我想出的解决方案。当然,它可以改进,因此可以接受批评。
我用了

await Dispatcher.Yield(DispatcherPriority.ApplicationIdle);

instead of Task.Yield()because Task.Yield()will make continuation a higher priority than GUI events, but, as a bad consequence, it demands your application to use WindowsBase.dll.

而不是Task.Yield()因为Task.Yield()将使延续比 GUI 事件具有更高的优先级,但是,作为一个糟糕的结果,它要求您的应用程序使用WindowsBase.dll.

public static class SshCommandExtensions
{
    public static async Task ExecuteAsync(
        this SshCommand sshCommand,
        IProgress<ScriptOutputLine> progress,
        CancellationToken cancellationToken)
    {
        var asyncResult = sshCommand.BeginExecute();
        var stdoutStreamReader = new StreamReader(sshCommand.OutputStream);
        var stderrStreamReader = new StreamReader(sshCommand.ExtendedOutputStream);

        while (!asyncResult.IsCompleted)
        {
            await CheckOutputAndReportProgress(
                sshCommand,
                stdoutStreamReader,
                stderrStreamReader,
                progress,
                cancellationToken);

            await Dispatcher.Yield(DispatcherPriority.ApplicationIdle);
        }

        sshCommand.EndExecute(asyncResult);

        await CheckOutputAndReportProgress(
            sshCommand,
            stdoutStreamReader,
            stderrStreamReader,
            progress,
            cancellationToken);
    }

    private static async Task CheckOutputAndReportProgress(
        SshCommand sshCommand,
        TextReader stdoutStreamReader,
        TextReader stderrStreamReader,
        IProgress<ScriptOutputLine> progress,
        CancellationToken cancellationToken)
    {
        if (cancellationToken.IsCancellationRequested)
        {
            sshCommand.CancelAsync();
        }
        cancellationToken.ThrowIfCancellationRequested();

        await CheckStdoutAndReportProgressAsync(stdoutStreamReader, progress);
        await CheckStderrAndReportProgressAsync(stderrStreamReader, progress);
    }

    private static async Task CheckStdoutAndReportProgressAsync(
        TextReader stdoutStreamReader,
        IProgress<ScriptOutputLine> stdoutProgress)
    {
        var stdoutLine = await stdoutStreamReader.ReadToEndAsync();

        if (!string.IsNullOrEmpty(stdoutLine))
        {
            stdoutProgress.Report(new ScriptOutputLine(
                line: stdoutLine,
                isErrorLine: false));
        }
    }

    private static async Task CheckStderrAndReportProgressAsync(
        TextReader stderrStreamReader,
        IProgress<ScriptOutputLine> stderrProgress)
    {
        var stderrLine = await stderrStreamReader.ReadToEndAsync();

        if (!string.IsNullOrEmpty(stderrLine))
        {
            stderrProgress.Report(new ScriptOutputLine(
                line: stderrLine,
                isErrorLine: true));
        }
    }
}

public class ScriptOutputLine
{
    public ScriptOutputLine(string line, bool isErrorLine)
    {
        Line = line;
        IsErrorLine = isErrorLine;
    }

    public string Line { get; private set; }

    public bool IsErrorLine { get; private set; }
}