我有一个C#函数来运行远程Powershell脚本。
我可以看到脚本正在运行,并且在远程服务器上运行良好,但我无法从Powershell获得任何实时输出。
ps1脚本运行很长时间并且有很多输出。该脚本正在运行rar.exe,因此输出应该来自rar.exe。
这是C#函数:
public static bool RunBackupPowershell()
{
string password = "somepassword";
string userName = "someuser";
string shell = "http://schemas.microsoft.com/powershell/Microsoft.PowerShell";
var target = new Uri("http://someserver:5985/wsman");
SecureString securepassword = String2SecureString(password);
PSCredential credential = new PSCredential(userName, securepassword);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(target,shell,credential);
connectionInfo.OpenTimeout = 4 * 60 * 1000;
connectionInfo.OpenTimeout = 1 * 60 * 1000;
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Credssp;
Runspace remote = RunspaceFactory.CreateRunspace(connectionInfo);
remote.Open();
PowerShell PowerShellInstance = PowerShell.Create();
PowerShellInstance.Runspace = remote;
PowerShellInstance.AddScript("D:\\backup\\test.ps1");
PSDataCollection<PSObject> outputCollection = new PSDataCollection<PSObject>();
outputCollection.DataAdded += outputCollection_DataAdded;
PowerShellInstance.Streams.Error.DataAdded += Error_DataAdded;
IAsyncResult result = PowerShellInstance.BeginInvoke<PSObject, PSObject>(null, outputCollection);
while (result.IsCompleted == false)
{
hubs.Clients.All.SendPowerShellResults("Waiting for pipeline to finish...");
Thread.Sleep(1000);
}
hubs.Clients.All.SendPowerShellResults("Execution has stopped. The pipeline state: " + PowerShellInstance.InvocationStateInfo.State);
foreach (PSObject outputItem in outputCollection)
{
hubs.Clients.All.SendPowerShellResults(outputItem);
}
return true;
}
static void outputCollection_DataAdded(object sender, DataAddedEventArgs e)
{
hubs.Clients.All.SendPowerShellResults("Object added to output."+e);
}
static void Error_DataAdded(object sender, DataAddedEventArgs e)
{
hubs.Clients.All.SendPowerShellResults("An error was written to the Error stream!"+e);
}
hubs.Clients.All.SendPowerShellResults
函数是一个websocket,它正在工作 - 所以我可以接收数据,但它不是来自Powershell输出的数据。
我想知道这里发生了什么。
答案 0 :(得分:0)
检查该主题是否有帮助:
Get Powershell command's output when invoked through code
流包含一个“ PsDataCollection”类型,每个项目都包含我们所需的输出。此类型包含“ DateAdded”事件。您可以使用DateAdded事件在添加数据后立即输出数据!
答案 1 :(得分:0)
我遇到了类似的问题,因此创建了下面的示例,以允许使用异步枚举实时检索结果以从 PowerShell 管道中获取结果。
https://gist.github.com/OnKey/83cf98e6adafe5a2b4aaf561b138087b
static async Task Main(string[] args)
{
var script = @"
For ($i=0; $i -le 5; $i++) {
$i
Start-Sleep -s 1
}
";
var p = new Program();
await foreach (var item in p.PowerShellAsyncEnumerable(script))
{
Console.WriteLine(item);
}
}
private IAsyncEnumerable<PSObject> PowerShellAsyncEnumerable(string script)
{
var rs = RunspaceFactory.CreateRunspace();
rs.Open();
var pipeline = rs.CreatePipeline();
pipeline.Commands.AddScript(script);
return new PsAsyncEnumerable(pipeline);
}
internal class PsAsyncEnumerable : IAsyncEnumerable<PSObject>
{
private readonly Pipeline pipe;
public PsAsyncEnumerable(Pipeline pipe) => this.pipe = pipe;
public IAsyncEnumerator<PSObject> GetAsyncEnumerator(CancellationToken cancellationToken = new())
=> new PsAsyncEnumerator(this.pipe);
}
internal class PsAsyncEnumerator : IAsyncEnumerator<PSObject>
{
private readonly Pipeline pipe;
private TaskCompletionSource dataReady = new();
public PsAsyncEnumerator(Pipeline pipe)
{
this.pipe = pipe;
this.pipe.Output.DataReady += NotificationHandler;
this.pipe.Error.DataReady += NotificationHandler;
this.pipe.InvokeAsync();
}
private void NotificationHandler(object sender, EventArgs e)
{
this.dataReady.SetResult();
}
public ValueTask DisposeAsync()
{
this.pipe.Dispose();
return ValueTask.CompletedTask;
}
public async ValueTask<bool> MoveNextAsync()
{
while (!this.pipe.Output.EndOfPipeline)
{
var item = this.pipe.Output.NonBlockingRead(1).FirstOrDefault();
if (item != null)
{
this.Current = item;
return true;
}
await this.dataReady.Task;
this.dataReady = new TaskCompletionSource();
}
return false;
}
public PSObject Current { get; private set; }
}