问题描述
在远程 Linux 机器上有一个长时间运行的脚本 script.sh
.我需要启动它并实时监控它的活动.脚本在其活动期间可能会输出到 stdout
和 stderr
.我正在寻找一种方法来捕获这两个流.
There is a long running script script.sh
on 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 stdout
and stderr
. I am searching for a way to capture both of the streams.
我使用 Renci SSH.NET 上传 script.sh
并启动它,所以很高兴看到绑定到这个库的解决方案.在我看来,完美的解决方案是新方法:
I use Renci SSH.NET to upload script.sh
and 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:
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();
}
});
推荐答案
所以,这是我想出的解决方案.当然,它可以改进,所以它是开放的批评.
我用过
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);
而不是 Task.Yield()
因为 Task.Yield()
将使延续比 GUI 事件具有更高的优先级,但是,作为一个坏结果,它要求您使用 WindowsBase.dll
的应用程序.
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
.
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; }
}
这篇关于SSH.NET实时命令输出监控的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!