Author Posts

June 7, 2018 at 8:23 pm

Sir/Madam,
I have a script of 6 lines as follows:
cls
#Get-PSDrive | Where-Object Description -eq "RECOVERY"
wmic logicaldisk get caption,volumename
Get-WmiObject win32_logicaldisk | select-object -property DeviceID,VolumeName
Get-WmiObject win32_logicaldisk | Where-object VolumeName -eq "DDRIVE"
Get-PSDrive | Where-Object Description -eq "RECOVERY"

When I run it, the last line never gets executed (in the sense that I didn't get anything on the console).
When I remove the '#' in Line 2 — which is identical to the last line, I get the outputs,
but in a seemingly strange order: the output of Line 2 and Line 6 (the last) appear together
at the end. I must be missing something about how console output is generated and handled.
Any advice or tips or references to the right source of help will be highly appreciated.

June 7, 2018 at 8:28 pm

Yeah, so, the thing you're running into is that PowerShell commands don't just emit a bunch of text. Each of your commands, except WMIC, emits objects, which PowerShell's formatting system has to try to turn into text. Because you're filling the pipeline with so many different kinds of objects, the formatting system is probably losing its mind at some point and just giving up.

Try running "Get-Service ; Get-Process" and you'll see how the formatting system can get a bit odd when you mix and match objects in the pipeline.

If your goal is to produce some kind of "report" or something, the correct (for PowerShell) approach would be to query everything, store the results in variables, and then combine whatever info you want into a consistent set of custom objects. Output those to the pipeline, and you end up with cleaner and more consistent output.

June 7, 2018 at 9:44 pm

Thank you very much Mr Jones!
Indeed, I changed the last line to add a pipe to:
Out-File zzz.txt
and the expected outputs were there. I have 2 more related questions.

Question-1:
I have been using files instead of variables, but generally, is it better to store such "temporary results" which are headed for more "textual massaging" in variables or files? The kind of temporary results I have in mind are not "big" nor "voluminous", i.e., no more than a few hundred lines, totalling no more than 10K bytes. If this question does not make sense, please ignore.

Question-2 (what is the syntax for a script block's contents?):

I still cannot find the documentation for the proper syntax inside a "script block" { }. For example:
(Get-content AAA.txt) | Foreach-object {_$ -replace "ooo","ZZZ"} | Set-content AAA.txt

What are the "allowable parameters" besides "-replace"? I am eager to know the full suite of what's allowed inside the { } block. Is there an IF-THEN-ELSE equivalents for simple processing?
I've already discovered the .NET methods that can be easily used (Contains, ToChar, Substring, Compare, etc.) but need to know where documentation exists for script blocks, specifically for Foreach-object { }.

Sincerest thanks to you for your help.
Best,
RTan

June 7, 2018 at 9:54 pm

I do something similar, here's a cut down version, see below, passing a list of servers in the $servers variable.

filter Get-CapacitySize {
   '{0:N2} {1}' -f $(
      if ($_ -lt 1kb) { $_, 'Bytes' }
      elseif ($_ -lt 1mb) { ($_/1kb), 'KB' }
      elseif ($_ -lt 1gb) { ($_/1mb), 'MB' }
      elseif ($_ -lt 1tb) { ($_/1gb), 'GB' }
      elseif ($_ -lt 1pb) { ($_/1tb), 'TB' }
      else { ($_/1pb), 'PB' }
   )
}

$GetData = {
[PSCustomObject]@{
        Volume = $(Get-Volume | Select-Object *)
        Disk = $(Get-Disk | Select-Object *)
    }
}

$snapshot  = Invoke-Command -ComputerName $Servers -ScriptBlock $GetData -ErrorAction SilentlyContinue

$snapshot | ForEach-Object{
    $ComputerName = $_.PSComputerName

    $_.Volume | ?{$_.FileSystemLabel -notlike "system*" -Or  [string]::IsNullOrWhiteSpace($_.DriveLetter)}  |
    ForEach-Object{
        [PSCustomObject]@{
            ComputerName = $ComputerName
            #DriveLetter = $_.DriveLetter
            DriveLetter = ($_.CimInstanceProperties[0] -replace('DriveLetter =','') -replace('"','')).Trim()
            FileSystemLabel = $_.FileSystemLabel 
            DiskSize = $_.Size | Get-CapacitySize
            UsedSpace = ($_.Size - $_.SizeRemaining) | Get-CapacitySize
            FreeSpace = $_.SizeRemaining | Get-CapacitySize
            "% Free" = "{0:P0}" -f  [math]::round(($_.SizeRemaining/$_.Size),2)
        }
    }
} | sort-object  { [INT]($_."% Free"  -replace '%')  } | Export-Excel -Path $xlsxFile -WorkSheetname Volumes -AutoSize -AutoFilter -FreezeTopRow -BoldTopRow