Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redirecting Standardoutput with Powershell class in .net

Tags:

c#

powershell

i am currently trying to get the standardoutput of a program I run via Powershell.

I have something like this:

                    using var ps = PowerShell.Create();
                    ps.AddCommand("Start-Process")
                        .AddParameter("FilePath", somePath)
                        .AddParameter("ArgumentList", someArguments)
                        .AddParameter(...); //Adding some more parameters

                    var result = ps.Invoke<Process>();
                    result[0].StandardOutput //Always null

Now I want to get the Standardoutput but I dont seem to be able to get it. The resulting Process has a public property "StandardOutput" but it is always null.

I know I can get this to work by adding a full Powershell script with AddScript instead of the other functions provided by the Powershell class.

$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = "SomeFileName"
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.Arguments = "someArguments"
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$p.WaitForExit()
$stdout = $p.StandardOutput.ReadToEnd()
Write-Host $stdout

I dont want to do this though because in my use case i am building those commands with parameters as needed and I dont want to resort to string operations to build my commands.

like image 984
Yuion aro Avatar asked Dec 04 '25 23:12

Yuion aro


1 Answers

Start-Process doesn't produce output, it offers this through the -RedirectStandard* parameters to redirect output to a file. If you need output from your process you should use the Process Class in your C# code as you're doing in your PowerShell code. Probably should leave a PowerShell instance out of the equation too as there is no need for it unless you want to make use of WriteError.

Another option could be running the process synchronously using the & operator in your PowerShell instance script.

Both methods are very rough examples for you to test, you should definitely implement error handling and other features if needed.

Save the definition in a PowerShell variable, i.e. $def:

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Management.Automation;

namespace Testing
{
    public static class Test
    {
        public static IEnumerable<string> RunWithProcess(string fileName, string[] args)
        {
            ProcessStartInfo psi = new ProcessStartInfo
            {
                UseShellExecute = false,
                RedirectStandardOutput = true,
                FileName = fileName,
                Arguments = string.Join(" ", args)
            };

            using (Process proc = new Process { StartInfo = psi })
            {
                proc.Start();
                proc.WaitForExit();

                while (!proc.StandardOutput.EndOfStream)
                {
                    yield return proc.StandardOutput.ReadLine();
                }
            }
        }

        public static Collection<PSObject> RunWithPwsh(string fileName, string[] args)
        {
            using (PowerShell ps = PowerShell.Create())
            {
                return ps.AddScript(@"
                    param($process, $arguments)

                    & $process @arguments")
                .AddParameter("process", fileName)
                .AddParameter("arguments", args)
                .Invoke();
            }
        }
    }
}
Add-Type -TypeDefinition $def

[Testing.Test]::RunWithPwsh('cmd.exe', ('/c', 'ping -n 10 google.com'))
[Testing.Test]::RunWithProcess('cmd.exe', ('/c', 'ping -n 10 google.com'))
like image 80
Santiago Squarzon Avatar answered Dec 07 '25 11:12

Santiago Squarzon