I\'m looking for a feature comparable to Python interactive shell\'s \"_\" variable. In PowerShell I want something like this:
> Get-Something # this returns
Not exactly. There's the $_ automatic value which contains the current object in the pipe-line.
The pipeline is the usual way to pass a result from one cmdlet to the next, and cmdlets are set to accept parameters from the pipeline or from the properties of objects in the pipeline making the use of a "last result" variable irrelevant.
Still some situations do require a specific reference to the "piped" object and for those there's the $_ automatic value.
Here's an example of its usage: Using the Where-Object Cmdlet and here's a list of powershell's automatic variables: Chapter 4. PowerShell Automatic Variables
Scripting in powershell requires a different style than programming in Python (same as Python requires a different style than C++.)
Powershell is built so pipes are used extensively, if you want to break up a pipeline into a more procedural step by step structure you'll need to save your results in named variables not automatic ones.
An approach that has worked for me is to process the command's output with Select-Object
as a pass-through. For instance, I had a console EXE that output verbose output to stderr
and meaningful output to stdout
, of which I wanted to capture only the last line.
$UtilityPath = ""
(UpdateUtility.exe $ArgList 2>&1) | % {
If ($_ -Is [System.Management.Automation.ErrorRecord]) {
$_.Exception.Message
}
Else {
$_
$UtilityPath = $_
}
}
The way this script was being invoked, having output on PowerShell's Error
output stream was considered a hard error, which didn't mesh nicely with the way PowerShell takes external apps' stderr
output and turns it into Error
output. This approach of wrapping the output allowed me to control how it passed through, as well as capture the stdout
line I wanted. It seems to me that this would be a fairly flexible approach that would allow you to intercept output and do anything you want with it as it passes through. For instance:
$CommandOutput = ""
SomeOtherCommand | % {
$CommandOutput += "$_`r`n"
$_
}
Use the module PowerShellCookbook and add a call to Add-ObjectCollector to your startup script
For my specific use case, I was running a batch file from PowerShell, and I wanted to print the output of that batch file in real-time AND save the output into a variable. I was able to accomplish this by piping the output of my call operator to Tee-Object
:
$args = @('-r', '-a');
& "C:\myFile.bat" $args | Tee-Object -Variable output;
$output | Set-Clipboard;
The first command sets up my arguments for the batch file. The second command runs the batch file using the call operator with my arguments, and it pipes the output to the Tee-Object
command, which prints the output in real-time from the call operator, but also saves all the information into a new variable called output. The last command simply copies the contents of $output
to the clipboard.
Tee-Object
also allows saving output to a file (Unicode encoding), and if I need to save to a file and a variable (in addition to printing to the console), I can chain multiple calls to Tee-Object
together in one pipeline. See this link for more information:
https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/tee-object
How about invoking the last command with "r" (alias for Invoke-History) and wrapping it in parentheses() to execute it first?
Yes this reruns the last command, but in my use cases this is by far the most streamlined solution especially for times when I don't realize that I'll need the last commands output at first.
It also keeps the object structure intact.
PS C:\Users\user> Get-NetAdapter -InterfaceIndex 3 | Where {$_.State -match "2"}
Name InterfaceDescription ifIndex Status MacAddress LinkSpeed
---- -------------------- ------- ------ ---------- ---------
Ethernet Intel(R) Ethernet Connection I217-LM 3 Up XX-XX-XX-XX-XX-XX 100 Mbps
PS C:\Users\user> (r) |where {$_.LinkSpeed -eq "100 Mbps"}
Get-NetAdapter -InterfaceIndex 3 | Where {$_.State -match "2"}
Name InterfaceDescription ifIndex Status MacAddress LinkSpeed
---- -------------------- ------- ------ ---------- ---------
Ethernet Intel(R) Ethernet Connection I217-LM 3 Up XX-XX-XX-XX-XX-XX 100 Mbps
PS C:\Users\user> (r).MacAddress
Get-NetAdapter -InterfaceIndex 3 | Where {$_.State -match "2"}
XX-XX-XX-XX-XX-XX
You can also print the result of the command and capture the output object(s) using the OutVariable parameter, later on then use $anObj to display the variable content.
Get-Something -OutVariable anObj