Reputation:
I am trying to write my first script but am having some issues with powershell.
I am using the following code:
$Disk = Get-Disk | Where-Object {$_.Path -match "USBSTOR" -and $_.Size -gt 20Gb -and $_.Size -lt 200Gb -and -not $_.IsBoot }
I can't figure out why
PS C:\> echo $disk
Number Friendly Name OperationalStatus Total Size Partition Style
------ ------------- ----------------- ---------- ---------------
1 Imation IronKey Wkspace USB Device Online 59.63 GB MBR
and
PS C:\> write-host $disk
MSFT_Disk (ObjectId = "\\?\usbstor#disk&ven_imation&prod_ironk...)
It is more complicated by the following powershell script:
$Disk = Get-Disk | Where-Object {$_.Path -match "USBSTOR" -and $_.Size -gt 20Gb -and $_.Size -lt 200Gb -and -not $_.IsBoot }
$WIM = Get-PSDrive -PSProvider FileSystem | Where { Test-Path (join-path $_.Root "\sources\install.wim") }
echo $Disk
echo $WIM
Write-Host $WIM
Write-Host $Disk
and then change the order of the echo's and writes I get different outputs
can someone explain what is going on?
Upvotes: 8
Views: 16725
Reputation: 95632
The Write-Host
cmdlet outputs to the host (i.e. the powershell or ISE window). It accepts an object as input and simply calls the .ToString()
method of the object (or if it is an array it calls .ToString()
or each element of the array.
Write-Output
or its alias echo
simply writes the input object to the current pipeline. Any objects that reach the end of the pipeline are then returned from the current function, cmdlet or script if there is one (where they may then continue down some other pipeline). Eventually objects may reach the end of a pipeline not inside a function, cmdlet or script and it appears they are output to the host. What actually happens is that at the end of the outermost pipeline all the objects are sent to an extra hidden cmdlet: Out-Default
.
Out-Default
sends the objects to the default formatter and then the default output cmdlet. It is the default formatter that converts your disk objects into a bunch of FormatStartData
, GroupStartData
, FormatEntryData
, GroupEndData
, and FormatEndData
objects (pipe the output of something like Format-Table
into Get-Member
to see these objects). Finally the format objects are what get sent to the host to produce the formatted output.
If you want a bit of fun try redefining Out-Default
so you can see where it is called:
PS C:\scripts> function Out-Default {
>> Write-Verbose "Called Out-Default" -Verbose
>> $input | Format-Table | Out-Host
>> }
>>
PS C:\scripts> cd mod2
VERBOSE: Called Out-Default
PS C:\scripts\mod2> ls
VERBOSE: Called Out-Default
Directory: C:\scripts\mod2
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 29/12/2013 11:00 308 1Var.ps1
-a--- 29/12/2013 11:00 326 2Quotes.ps1
-a--- 29/12/2013 11:00 416 3ObjectMembers.ps1
-a--- 29/12/2013 11:00 665 4Parenthesis.ps1
-a--- 29/12/2013 11:00 392 5If.ps1
-a--- 29/12/2013 11:00 325 6Switch.ps1
-a--- 29/12/2013 11:00 226 7Do_While.ps1
-a--- 29/12/2013 11:00 272 8For_Foreach.ps1
-a--- 29/12/2013 11:00 150 _Startup.ps1
Upvotes: 5
Reputation: 26003
In PowerShell, echo
maps to Write-Output instead of Write-Host. If you want to output the objects resulting from some operation, you should be outputting them instead of attempting to Write-Host
. Content written via Write-Host
doesn't make it to the output stream, so you can't (for example) use it in subsequent calls or pipes.
You see that nasty garbled output with Write-Host
because it is trying to write the whole object out, and doesn't understand that you would probably prefer a nice table with cleanly listed properties. You should reserve Write-Host
for when you need to convey some simple information to the user that you don't mind being consumed, and even then there may be better alternatives like Write-Debug or Write-Verbose.
More info here.
You can always check mappings like this by calling a cmdlet with Help
or Get-Help
:
PS C:\> help echo
NAME
Write-Output
Upvotes: 10