Senger CodeLab 🚀

How do I capture the output into a variable from an external process in PowerShell

September 29, 2026

📂 Categories: Programming
How do I capture the output into a variable from an external process in PowerShell

PowerShell, with its robust automation capabilities, is a sysadmin’s best friend. But what happens when you need to wrangle the output of an external command, say a ping or a complex script, and store it for later use? This is where capturing output into a variable becomes essential. Mastering this technique unlocks a whole new level of control and flexibility in your PowerShell scripts, allowing you to manipulate, analyze, and report on data with ease. This guide will dive deep into various methods for capturing output, explaining the nuances and providing real-world examples to help you become a PowerShell pro.

Using the Assignment Operator

The simplest way to capture output is using the assignment operator (=). This directly assigns the standard output of a command to a variable. This works well for simple commands, but it’s important to be aware that this method captures only the standard output stream (stdout). Error messages (stderr) are not captured this way.

For example, let’s capture the output of the Get-ChildItem cmdlet:

$files = Get-ChildItem

Now the $files variable contains a list of all files and folders in the current directory. This is fundamental for automating file system operations.

Capturing Output with Redirection

Redirection provides more granular control over output streams. You can redirect stdout, stderr, or both to separate variables. This is crucial when you need to handle errors effectively.

For instance, to capture both standard output and errors:

$output = & ping google.com 2>&1

The 2>&1 redirects stderr to stdout, and the entire output is stored in $output. Understanding redirection is key for robust script writing.

Working with External Commands

When dealing with external commands (like ping, ipconfig, or even batch scripts), the same principles apply. You can capture their output using the assignment operator or redirection.

Consider this example using ping:

$pingResult = ping google.com

This stores the ping results in the $pingResult variable. You can then parse this output to check for connectivity issues or latency.

Advanced Output Capture Techniques

For more complex scenarios, PowerShell offers advanced techniques like using the Start-Process cmdlet with the -RedirectStandardOutput parameter. This provides greater control over the execution environment, especially useful for long-running processes or those requiring specific credentials.

Example:

$process = Start-Process ping -ArgumentList "google.com" -RedirectStandardOutput output.txt -Wait<br></br> $output = Get-Content output.txt

This redirects the output to a file and then reads the file contents into a variable. A practical scenario might be capturing the output of a lengthy database query to a file for later analysis.

Parsing and Utilizing Captured Output

Once captured, the variable’s contents can be manipulated using PowerShell’s string processing capabilities, regular expressions, or by converting it to an object for property access. This allows you to extract specific information, generate reports, or use the data to automate further tasks.

For instance, you could extract the IP address from the $pingResult variable.

Real-World Example: Log Analysis

Imagine needing to analyze log files for specific errors. You can use PowerShell to capture the log file’s content into a variable, then use regular expressions to find and count error instances, ultimately automating a tedious task and providing valuable insights.

  • Streamlining repetitive tasks.
  • Improving accuracy by eliminating manual processes.
  1. Capture the output.
  2. Parse the output.
  3. Utilize the extracted information.

“Automation is key to efficient system administration. Capturing and manipulating command output is fundamental to this process.” - PowerShell Expert

Placeholder for Infographic: Illustrating different output capturing methods.

Another valuable resource you can visit is this blog post, anchor text which offers a wide range of practical tips on how to properly handle different types of errors in PowerShell scripts.

External Resources:

Featured Snippet Optimized Paragraph: To capture output in a PowerShell variable, use the assignment operator (=) for simple commands or redirection for more control over output streams. $output = command captures stdout, while $output = & command 2>&1 captures both stdout and stderr.

FAQ

Q: What’s the difference between stdout and stderr?

A: Stdout is the standard output stream for normal program output. Stderr is the standard error stream for error messages.

By mastering these output capture techniques, you’ll significantly enhance your PowerShell scripting capabilities and unlock a new level of automation. Start experimenting with these methods today, and you’ll quickly discover how powerful they can be. Explore more advanced topics like using the .NET framework within PowerShell for even greater control over external processes. Dive deeper into regular expressions for more sophisticated output parsing. The possibilities are endless.

Question & Answer :
I’d like to run an external process and capture its command output to a variable in PowerShell. I’m currently using this:

$params = "/verify $pc /domain:hosp.uhhg.org" start-process "netdom.exe" $params -WindowStyle Hidden -Wait 

I’ve confirmed the command is executing but I need to capture the output into a variable. This means I can’t use the -RedirectOutput because this only redirects to a file.

Note: The command in the question uses Start-Process, which prevents direct capturing of the target program’s output. Generally, do not use Start-Process to execute console applications synchronously - just invoke them directly, as in any shell. Doing so keeps the application’s output streams connected to PowerShell’s streams, allowing their output to be captured by simple assignment $output = netdom ... (and with 2> for stderr output), as detailed below.

Fundamentally, capturing output from external programs works the same as with PowerShell-native commands (you may want a refresher on how to execute external programs; <command> is a placeholder for any valid command below):

# IMPORTANT: # <command> is a *placeholder* for any valid command; e.g.: # $cmdOutput = Get-Date # $cmdOutput = attrib.exe +R readonly.txt $cmdOutput = <command> # captures the command's success stream / stdout output 

Note that $cmdOutput receives an array of objects if <command> produces more than 1 output object, which in the case of an external program means a string[1] array containing the program’s output lines.

If you want to make sure that the result is always an array - even if only one object is output, type-constrain the variable as an array ([object[]]), or enclose the command in @(...), the array-subexpression operator:[2]

[array] $cmdOutput = <command> $cmdOutput = @(<command>) # alternative 

By contrast, if you want $cmdOutput to always receive a single - potentially multi-line - string, use Out-String, though note that a trailing newline is invariably added (GitHub issue #14444 discusses this problematic behavior):

# Note: Adds a trailing newline. $cmdOutput = <command> | Out-String 

With calls to external programs - which by definition only ever return strings in PowerShell[1] - you can avoid that by using the -join operator instead:

# NO trailing newline. $cmdOutput = (<command>) -join "`n" 

Note: For simplicity, the above uses "n"to create Unix-style LF-only newlines, which PowerShell happily accepts on all platforms; if you need platform-appropriate newlines (CRLF on Windows, LF on Unix), use[Environment]::NewLine` instead.


To capture output in a variable and print to the screen:

<command> | Tee-Object -Variable cmdOutput # Note how the var name is NOT $-prefixed 

Or, if <command> is a cmdlet or advanced function, you can use common parameter
-OutVariable / -ov
:

<command> -OutVariable cmdOutput # cmdlets and advanced functions only 

Note that with -OutVariable, unlike in the other scenarios, $cmdOutput is always a collection, even if only one object is output. Specifically, an instance of the array-like [System.Collections.ArrayList] type is returned.
See this GitHub issue for a discussion of this discrepancy.


To capture the output from multiple commands, use either a subexpression ($(...)) or call a script block ({ ... }) with & or .:

$cmdOutput = $(<command>; ...) # subexpression $cmdOutput = & {<command>; ...} # script block with & - creates child scope for vars. $cmdOutput = . {<command>; ...} # script block with . - no child scope 

Note that the general need to prefix with & (the call operator) an individual command whose name/path is quoted - e.g., $cmdOutput = & 'netdom.exe' ... - is not related to external programs per se (it equally applies to PowerShell scripts), but is a syntax requirement: PowerShell parses a statement that starts with a quoted string in expression mode by default, whereas argument mode is needed to invoke commands (cmdlets, external programs, functions, aliases), which is what & ensures.

The key difference between $(...) and & { ... } / . { ... } is that the former collects all input in memory before returning it as a whole, whereas the latter stream the output, suitable for one-by-one pipeline processing.


Redirections also work the same, fundamentally (but see caveats below):

$cmdOutput = <command> 2>&1 # redirect error stream (2) to success stream (1) 

However, for external commands the following is more likely to work as expected:

$cmdOutput = cmd /c <command> '2>&1' # Let cmd.exe handle redirection - see below. 

Considerations specific to external programs:

  • External programs, because they operate outside PowerShell’s type system, only ever return strings via their success stream (stdout); similarly, PowerShell only ever sends strings to external programs via the pipeline.[1]

    • Character-encoding issues can therefore come into play:
      • On sending data via the pipeline to external programs, PowerShell uses the encoding stored in the $OutVariable preference variable; which in Windows PowerShell defaults to ASCII(!) and in PowerShell [Core] to UTF-8.
      • On receiving data from an external program, PowerShell uses the encoding stored in [Console]::OutputEncoding to decode the data, which in both PowerShell editions defaults to the system’s active OEM code page.
      • See this answer for more information; this answer discusses the still-in-beta (as of this writing) Windows 10 feature that allows you to set UTF-8 as both the ANSI and the OEM code page system-wide.
  • If the output contains more than 1 line, PowerShell by default splits it into an array of strings. More accurately, the output lines are streamed one by one, and, when captured, stored in an array of type [System.Object[]] whose elements are strings ([System.String]).

  • If you want the output to be a single, potentially multi-line string, use the -join operator (you can alternatively pipe to Out-String, but that invariably adds a trailing newline):
    $cmdOutput = (<command>) -join [Environment]::NewLine

  • Merging stderr into stdout with 2>&1, so as to also capture it as part of the success stream, comes with caveats:

    • To do this at the source, let cmd.exe handle the redirection, using the following idioms (works analogously with sh on Unix-like platforms):
      $cmdOutput = cmd /c <command> '2>&1' # *array* of strings (typically)
      $cmdOutput = (cmd /c <command> '2>&1') -join "rn" # single string

      • cmd /c invokes cmd.exe with command <command> and exits after <command> has finished.
      • Note the single quotes around 2>&1, which ensures that the redirection is passed to cmd.exe rather than being interpreted by PowerShell.
      • Note that involving cmd.exe means that its rules for escaping characters and expanding environment variables come into play, by default in addition to PowerShell’s own requirements; in PS v3+ you can use special parameter --% (the so-called stop-parsing symbol) to turn off interpretation of the remaining parameters by PowerShell, except for cmd.exe-style environment-variable references such as %PATH%.
      • Note that since you’re merging stdout and stderr at the source with this approach, you won’t be able to distinguish between stdout-originated and stderr-originated lines in PowerShell; if you do need this distinction, use PowerShell’s own 2>&1 redirection - see below.
    • Use PowerShell’s 2>&1 redirection to know which lines came from what stream:

      • Stderr output is captured as error records ([System.Management.Automation.ErrorRecord]), not strings, so the output array may contain a mix of strings (each string representing a stdout line) and error records (each record representing a stderr line). Note that, as requested by 2>&1, both the strings and the error records are received through PowerShell’s success output stream).

      • Note: The following only applies to Windows PowerShell - these problems have been corrected in PowerShell [Core] v6+, though the filtering technique by object type shown below ($_ -is [System.Management.Automation.ErrorRecord]) can also be useful there.

      • In the console, the error records print in red, and the 1st one by default produces multi-line display, in the same format that a cmdlet’s non-terminating error would display; subsequent error records print in red as well, but only print their error message, on a single line.

      • When outputting to the console, the strings typically come first in the output array, followed by the error records (at least among a batch of stdout/stderr lines output “at the same time”), but, fortunately, when you capture the output, it is properly interleaved, using the same output order you would get without 2>&1; in other words: when outputting to the console, the captured output does NOT reflect the order in which stdout and stderr lines were generated by the external command.

      • If you capture the entire output in a single string with Out-String, PowerShell will add extra lines, because the string representation of an error record contains extra information such as location (At line:...) and category (+ CategoryInfo ...); curiously, this only applies to the first error record.

        • To work around this problem, apply the .ToString() method to each output object instead of piping to Out-String:
          $cmdOutput = <command> 2>&1 | % { $_.ToString() };
          in PS v3+ you can simplify to:
          $cmdOutput = <command> 2>&1 | % ToString
          (As a bonus, if the output isn’t captured, this produces properly interleaved output even when printing to the console.)
        • Alternatively, filter the error records out and send them to PowerShell’s error stream with Write-Error (as a bonus, if the output isn’t captured, this produces properly interleaved output even when printing to the console):
$cmdOutput = <command> 2>&1 | ForEach-Object { if ($_ -is [System.Management.Automation.ErrorRecord]) { Write-Error $_ } else { $_ } } 

An aside re argument-passing, as of PowerShell 7.2.x:

  • Passing arguments to external programs is broken with respect to empty-string arguments and arguments that contain embedded " characters.
  • Additionally, the (nonstandard) quoting needs of executables such as msiexec.exe and batch files aren’t accommodated.

For the former problem only, a fix may be coming (though the fix would be complete on Unix-like platforms), as discussed in this answer, which also details all the current problems and workarounds.

If installing a third-party module is an option, the ie function from the Native module (Install-Module Native) offers a comprehensive solution.


[1] As of PowerShell 7.1, PowerShell knows only strings when communicating with external programs. There is generally no concept of raw byte data in a PowerShell pipeline. If you want raw byte data returned from an external program, you must shell out to cmd.exe /c (Windows) or sh -c (Unix), save to a file there, then read that file in PowerShell. See this answer for more information.

[2] There are subtle differences between the two approaches (which you may combine), though they usually won’t matter: If the command has no output, the [array] type-constraint approach results in $null getting stored in the target variable, whereas it is an empty ([object[]) array in the case of @(...). Additionally, the [array] type constraint means that future (non-empty) assignments to the same variable are coerced to an array too.