Why can't I loop through my disk size? - powershell

This is very simple code that I can't seem to make function properly
I want to find all the drives on my computer then, want to display their size.
$disks = gwmi win32_LogicalDisk | Where {$_.DriveType -eq "3"} | Select DeviceID
foreach ($i in $disks){
$i
gwmi win32_logicaldisk | Where {$_.DeviceID -eq "$i"} | Select Size
}
When displaying $i, the output is exactly as I need it I even used a Read-Host to check them one by one and every drive letter is displayed. Unfortunetly, the command to Select the size does not return anything.
Thanks for the help!

It would be more intuitive to me to iterate through disks themselves, not their IDs, i.e.
$disks = gwmi win32_LogicalDisk | Where {$_.DriveType -eq "3"}
foreach ($disk in $disks)
{
$disk.Size
}
$disk | Select Size also works, and, as Joey neatly complemented, whole thing is equivalent to
gwmi win32_LogicalDisk | Where {$_.DriveType -eq "3"} | Select -ExpandProperty Size

I don't know powershell really, but I figured I'd mess around with your script. Looks to me like the "$i" is wrong - instead use $i.DeviceID (without the quotes). Whole script that seems to work for me:
$disks = gwmi Win32_LogicalDisk | Where {$_.DriveType -eq "3"} | Select DeviceID
foreach ($i in $disks){
$i.DeviceID
gwmi Win32_LogicalDisk | Where {$_.DeviceID -eq $i.DeviceID} | Select Size
}
Another option would be to skip the iteration:
gwmi Win32_LogicalDisk | Where {$_.DriveType -eq "3"} | Select DeviceID, Size
Algorithmically I would recommend this based on execution -- the approach you are taking now iterates over all of the devices two times inside loops (basically O(n^2)). The "other option" above only iterates over them once.

Try to use server side filtering when ever you can using the Filter parameter instead of the Where-Object cmdlet (client side filtering), it performs faster. The latter gets ALL objects first and only then performs the filtering.
Get-WmiObject Win32_LogicalDisk -Filter "DriveType=3 AND DeviceId='$($i):'" | Select-Object Size

Related

How to execute several cmdlets in powershell script?

I have written this script to get some information about my Virtual Machine. When I execute this script the last two cmdlets (lines) don't execute, but when I execute them alone they run properly.
Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3" | Select-Object DeviceID, SystemName,
#{n='Size (GB)'; e={$_.Size / 1GB -as [int]}},
#{n='Freespace (GB)'; e={$_.Freespace / 1GB -as [int]}};
$TotalMemory = (Get-CimInstance Win32_OperatingSystem).TotalVisibleMemorySize / (1024*1024)
$UsedMemory = (Get-CimInstance Win32_OperatingSystem).FreePhysicalMemory / (1024*1024)
$TotalMemory = [math]::Round($TotalMemory,2)
$UsedMemory = [math]::Round($UsedMemory,2)
$TotalMemory
$UsedMemory
Get-CimInstance Win32_Processor | Select-Object -Property NumberOfCores;
Get-CimInstance Win32_Processor | Measure-Object -Property LoadPercentage -Sum | Select-Object Sum;
What is the problem?
Any comments would be appreciated.
This question has been asked a million times. Format-table is being implicitly run, and it doesn't handle different sets of columns well. All the objects are there. You can pipe the whole script to format-list. You can put get-date at the very beginning. A known object with a format file at the beginning fixes it. You can output more than 4 properties with the first object. I tried to ask for a warning to be added a while ago: format-table should at least warn when it doesn't display properties #7871
Looks like out-gridview has similar struggles: Not all properties displayed
This appears to be one of PowerShell's weird quirks. I am not sure why it happens, but if someone else does I will happily edit my answer to include it.
You will need to specifically tell it to write the output to the console. There are a couple ways of doing this.
The first, and probably messiest, would be to pass it as a parameter to Write-Host or Write-Output.
Write-Host (Get-CimInstance Win32_Processor | Select-Object -Property NumberOfCores)
Write-Host (Get-CimInstance Win32_Processor | Measure-Object -Property LoadPercentage -Sum | Select-Object Sum)
(Another way of doing this would be to assign them both to variables, and pass the variables to the cmdlet)
The second way would be to pipe it to Write-Host or Write-Output
Get-CimInstance Win32_Processor | Select-Object -Property NumberOfCores | Write-Host
Get-CimInstance Win32_Processor | Measure-Object -Property LoadPercentage -Sum | Select-Object Sum | Write-Host
And finally, the third (and in my opinion cleanest) way would be to pipe it to a code block that references the property directly
Get-CimInstance Win32_Processor | %{$_.NumberOfCores}
Get-CimInstance Win32_Processor | Measure-Object -Property LoadPercentage -Sum | %{$_.Sum}
Hope this helps!

Powershell -lt and -gt giving opposite of expected results [duplicate]

This question already has an answer here:
How can I store output from Format-Table for later use
(1 answer)
Closed 3 years ago.
In the below code, if I add a where-object, -lt & -gt are giving opposite of expected results.
I'm sure the reason is I'm stupid, but in what specific way am I messing up?
This part gives the expected results, where in my case, the single drive has %Free of 39.8
Get-WmiObject -Namespace root\cimv2 -Class win32_logicaldisk | where-object -Property drivetype -eq 3 |
format-table deviceid,
#{n='GB Capacity';e={$_.size/1gb}},
#{n='GB Free';e={$_.freespace/1gb}},
#{n='%Free';e={($_.freespace/$_.size)*100}}
But adding this
| where {$_.'%Free' -gt 10}
Results in no output. In fact
| where {$_.'%Free' -gt 0}
Produces no results. Instead I have to use
| where {$_.'%Free' -lt 0}
Powershell thinks %Free is a negative number, I guess?
The problem is that you are piping Format-Table to anything. You should never use that except to output to the screen. Using Format-Table outputs everything as a format object, not whatever was piped into it. Instead use Select-Object to get what you need.
Get-WmiObject -Namespace root\cimv2 -Class win32_logicaldisk | where-object -Property drivetype -eq 3 |
Select-Object deviceid,
#{n='GB Capacity';e={$_.size/1gb}},
#{n='GB Free';e={$_.freespace/1gb}},
#{n='%Free';e={($_.freespace/$_.size)*100}}

How can I get the machine IP address only for several PCs using ForEach-Object in Powershell?

I am working on a script to get the IP address information from several computers using the ForEach statement. Problem is that the IP address string returns extra info, so in order to get rid of that info, I run this...
Get-WmiObject win32_networkadapterconfiguration |
where { $_.ipaddress -like "1*" } |
select -ExpandProperty ipaddress |
select -First 1}
Which is fine for one computer but when I add it to the whole ForEach part of my script below, it returns the IP Address for my machine attached to all machine queried in the script.
Get-Content $MultiActivePing | foreach {
Get-WmiObject win32_networkadapterconfiguration -ComputerName $_ |
where { $_.ipaddress -ne $null} |
select #{Expression={$_.__SErver};Label="Computer"}
,#{Expression={Get-WmiObject win32_networkadapterconfiguration | where { $_.ipaddress -like "1*" } | select -ExpandProperty ipaddress | select -First 1};Label="IPAddress"} `
,#{Expression={$_.DefaultIPGateway};Label="Gateway"}
}
So I was wondering if there was a way to expand the property of the IP address without having to run another GWMI inside the SELECT IP address portion.
Admitting I am a little confused since I don't know what you are expecting exactly however I can venture a guess. Mostly I'm not sure what you want the IP Address output to look like.
Get-Content $MultiActivePing | ForEach-Object {
# Save the current computer name so we can use down the line.
$computer = $_
Get-WmiObject win32_networkadapterconfiguration -ComputerName $_ |
where { $_.ipaddress -ne $null} |
Select #{l="IPAddress";e={$_.ipaddress | Select -First 1}},#{l="Computer";e={$computer}},DHcpenabled,macaddress
}
The part about this that will vary depending on what you are trying to accomplish:
Select #{l="IPAddress";e={$_.ipaddress | Select -First 1}}
You already know the IP so there is no need to acquire it again. This takes the first IP in the array and outputs it alone. This is the part I think you are having issues with but I don't know how you choose what information to drop. The caveat obviously is that you could lose IP information that you actually want depending on the order it appears. I would suggest this then as well so nothing is lost:
Change the calculated property for IPAddress highlighted above to this:
#{l="IPAddresses";e={$_.ipaddress -Join ";"}}
That would take all IP's and convert them into a semicolon delimited string
So thanks to Matt's logic, I just added this to the script...
,#{E={$_.ipaddress | Select -First 1};L="IPAddress"}
Here's what it looks like now, something so simple. Took care of my issue...
ForEach {
Get-WmiObject win32_networkadapterconfiguration -ComputerName $_ |
where { $_.ipaddress -ne $null} |
Select #{E={$_.__SErver};L="Computer"}
,#{E={$_.ipaddress | Select -First 1};L="IPAddress"}
,#{E={$_.DefaultIPGateway | Select -First 1};L="Gateway"}

letter of first logical disk (PowerShell)

How can I get a letter of first logical disk in PowerShell? I need to assign it to the variable.
So far I have:
$drive = Get-WmiObject -Class win32_volume
If you actually want logical disk information you should query win32_logicaldisk
Get-WmiObject win32_logicaldisk -filter "Drivetype=3"
3 being the Local Disk
If you only wanted the "first" you could pipe it into Select-Object
Get-WmiObject win32_logicaldisk -filter "Drivetype=3" | Select-Object -First 1
If you then still needed information from win32_volume you could do this
$firstDisk = Get-WmiObject win32_logicaldisk -filter "Drivetype=3" | Select-Object -First 1 -ExpandProperty caption
Get-WmiObject -Class win32_volume | Where-Object{$_.DriveLetter -eq $firstDisk}
Use this command:
Get-WmiObject win32_logicaldisk -ComputerName $pc -filter "Drivetype=3"
Depends what else you want to use it with of course, it should give you what you're looking for
Just for funs sake here is another proposal:
Get-PSDrive -PSProvider FileSystem | where {$_.Used -ne $null} | select -First 1
MattĀ“s answer is the way to go though

How can I extract "Path to executable" of all services with PowerShell

Get-Service *sql* | sort DisplayName | out-file c:/servicelist.txt
I have a one line PowerShell script to extract list of all services running on my local machine, now, in addition to displaying "Status", "Name" and "DisplayName" I also want to display "Path to executable"
I think you'll need to resort to WMI:
Get-WmiObject win32_service | ?{$_.Name -like '*sql*'} | select Name, DisplayName, State, PathName
Update
If you want to perform some manipulation on the selected data, you can use calculated properties as described here.
For example if you just wanted the text within quotes for the Pathname, you could split on double quotes and take the array item 1:
Get-WmiObject win32_service | ?{$_.Name -like '*sql*'} | select Name, DisplayName, #{Name="Path"; Expression={$_.PathName.split('"')[1]}} | Format-List
Get-CimInstance can also be used to achieve the same, see here for the difference between CIM and WMI.
Get-CimInstance win32_service | ?{$_.Name -like '*sql*'} | select Name, DisplayName, #{Name="Path"; Expression={$_.PathName.split('"')[1]}} | Format-List
Since Get-WmiObject have been deprecated in PowerShell Core, you can use
Get-CimInstance -ClassName win32_service | ?{$_.Name -match '^sql'} | Select Name, DisplayName, State, PathName >> C:\temp\sqlservices.txt
instead.
If you don't need to check against a regular expression you can also use the -Filter parameter:
Get-CimInstance -ClassName win32_service -Filter "Name like 'sql%'" | Select Name, DisplayName, State, PathName >> C:\temp\sqlservices.txt
A variant on the WMI Query that may be faster (I just had to do this for an SCCM Client)
$SQLService=(get-wmiobject -Query 'Select * from win32_service where Name like "*SQL*"') | Select-object Name, DisplayName, State, Pathname
The other trick is to trap for the multiple SQL results if you want the path names without the Double Quotes (so you can action upon them)
$SQLService | Select-Object Name, DisplayName, State, #{Name='PathName';Expression=$_.Pathname.replace('"','')}
The big advantage to using -query in the get-wmiobject (or get-ciminstance) is the speed of processing. The older example gets a full list and then filters, whilst the latter grabs a very direct list.
Just adding in two cents :)
Cheers all!
Sean
The Energized Tech
You can also use the Regular Expression pattern and dump the result to file.
Get-WmiObject win32_service | ?{$_.Name -match '^sql'} | select Name, DisplayName, State, PathName >> C:\temp\sqlservices.txt
I'm not comfortable with the accepted answer's use of Expression={$_.PathName.split('"')[1]}} because it doesn't handle the variants of quotes, spaces, and args that I see in the data.
Here's a clunky method that does.
function PathFromServicePathName($pathName) {
# input can have quotes, spaces, and args like any of these:
# C:\WINDOWS\system32\lsass.exe
# "C:\Program Files\Realtek\Audio\HDA\RtkAudioService64.exe"
# C:\WINDOWS\system32\svchost.exe -k netsvcs -p
# "C:\Program Files\Websense\Websense Endpoint\wepsvc.exe" -k ss
# if it starts with quote, return what's between first and second quotes
if ($pathName.StartsWith("`"")) {
$pathName = $pathName.Substring(1)
$index = $pathName.IndexOf("`"")
if ($index -gt -1) {
return $pathName.Substring(0, $index)
}
else {
# this should never happen... but whatever, return something
return $pathName
}
}
# else if it contains spaces, return what's before the first space
if ($pathName.Contains(" ")) {
$index = $pathName.IndexOf(" ")
return $pathName.Substring(0, $index)
}
# else it's a simple path
return $pathName
}
Get-WmiObject win32_service | select Name, DisplayName, #{Name="Path"; Expression={PathFromServicePathName $_.PathName}} | Format-List
A variant with Format-List with full path, results in file :
Get-WmiObject win32_service | Format-Table -Wrap -AutoSize -Property State,Name,PathName | out-file C:\servicelist.txt