How to output get-content to array in powershell? - arrays

I have the following code:
get-content C:\file.txt | Foreach{($_ | Select-String "$" -all).Matches | measure | select count}
I want to find the number of $ in the file on each line, which works successfully, I am given a tabled count of the number of each character per line. However, I want to output each as a value to an array and perform an arithmetic operation on the count. As it stands everything I've tried has given me a multidimensional array in stead of an array of integers.
For example I've tried
$counts = #(get-content C:\ampersand.txt | Foreach{($_ | Select-String "&" -all).Matches | measure | select count} )
but that just spits out a multidimensional array which I can't perform an arithmetic operation on

I think this is just one of the PowerShell gotcha's. $Counts is not an integer array but an object array with a count property.
get-content C:\file.txt | Foreach{($_ | Select-String "$" -all).Matches | measure | select-object -ExpandProperty count}

Related

Sort an array numerically by extracting integers in names

$Folders = Get-ChildItem -LiteralPath $PSScriptRoot | Where-Object {$_.PSIsContainer} | Select-Object -ExpandProperty BaseName
I get the output
Set 1
Set 10
Set 11 - A Memo
Set 2
Set 20
Set 22 - A Memo With Numbers 1234
Set 3
Set 33 - A Memo
...
$Folders = $Folders | Sort-Object {[INT]($_ -Replace 'Set ', '')} will sort the names in the right order but doesn't work if there is anything after the number like ' - A Memo'.
I've tried \b\d+\b on https://regexr.com but don't know how to implement that in this case.
I need a regex that can extract the number after 'Set ' and discard everything else.
RegEx is a whole other language in itself
Some alternatives for extracting the number, complementing g.sulman's excellent answer.
First the most simplest method, assuming "Set" and the number are always separated by space:
$Folders | Sort-Object { [int]($_ -split ' ')[1] }
This uses the -split operator to split the string on space character, which returns an array. Then it converts the 2nd element to int.
Use -match operator:
$Folders | Sort-Object { [int]( $_ -match '\d+' ? $matches[0] : 0 ) }
Note that conditional operator ? requires PS 7. Alternative for older PS versions:
$Folders | Sort-Object { [int]( if( $_ -match '\d+' ){ $matches[0] } else { 0 } ) }
The -match operator finds the first sub string that matches the RegEx \d+ which stands for one or more digits. The found sub string can be accessed through $matches[0].
Use Select-String cmdlet:
$Folders | Sort-Object { [int] ( $_ | Select-String -Pattern \d+ ).Matches[0].Value }
Same principle as the -match method. Just a different way to access the found sub string.
$names = #"
Set 1
Set 10
Set 11 - A Memo
Set 2
Set 20
Set 22 - A Memo With Numbers 1234
Set 3
Set 33 - A Memo
"# -split "`n"
$names | sort #{expression={[int]($_ -replace '^\w+\s|\s.+')}}
You can use an expression with Sort-Object. Above this is done to replace everything you don't care about and convert to int for number sorting (in text sorting 1, 10, 11, 2, 20 ... is expected.)
Regex breakdown
^ - start of the string
\w - word character (matches S)
+ - the previous thing as many times as need (matches Se, Set, Seet, Seeeeeeeet)
\s - space
| - or. so either everything before this, or everything after
\s - space
. - any character
+ - I think this one's covered above
Note: + matches 1 or more. Use * if you need to match 0 or more.
Edit: As per zett42's helpful comment, you could use [int]($_ -split ' ')[1] in the Sort-Object expression. This splits your name into an array, and takes the 2nd element of that array.

Powershell Group-object array list

Two comma separated item added in array list and I would like to group them to count the total.
$list_distinct = [System.Collections.ArrayList]#()
$list_distinct.Add("Site A,Item A")
$list_distinct.Add("Site A,Item A")
$list_distinct.Add("Site A,Item B")
$list_distinct.Add("Site B,Item C")
$list_distinct.Add("Site B,Item D")
$list_distinct.Add("Site B,Item D")
Tried this:
$test = $list_distinct | Group-Object Values
The result shows Count (the whole total), Name(empty) and Group (the whole added items).
Any way to fix this? Or is there any better method?
Desired output example:
Site | Item | Count
Site A | Item A | 2
Site A | Item B | 1
Site B | Item C | 1
Site B | Item D | 2
Neither the ArrayList object nor its elements have a property Values. Non-existent properties are expanded to an empty result, so all of your values are grouped under the same (empty) name.
Change this
$list_distinct | Group-Object Values
into this
$list_distinct | Group-Object
and the problem will disappear.
For your desired output you will also need to split the values and create new (custom) objects:
$list_distinct | Group-Object | ForEach-Object {
$site, $item = $_.Name -split ','
New-Object -Type PSObject -Property #{
'Site' = $site
'Item' = $item
'Count' = $_.Count
}
} | Select-Object Site, Item, Count
The trailing Select-Object is to enforce field order since PowerShell hashtables aren't ordered by default.
In PowerShell v3 and newer you can simplify that to
$list_distinct | Group-Object | ForEach-Object {
$site, $item = $_.Name -split ','
[PSCustomObject]#{
'Site' = $site
'Item' = $item
'Count' = $_.Count
}
}
The trailing Select-Object isn't needed here, because the [PSCustomObject] type accelerator implicitly uses an ordered hashtable.

Element ID of object in Powershell array

I have an array of objects, a list of computers, so the array looks something like this:
+----------+-----------+---------------+
| size(MB) | Computer | Location |
+----------+-----------+---------------+
| 100 | compname1 | C:\Users\etc\ |
| Offline | compname2 | Offline |
| 30 | compname3 | C:\users\etc2 |
+----------+-----------+---------------+
I need to iterate through these and any that are offline, scan them again. The issue I have is that on each iteration of the loop, how do I find the element number of the current object in the array?
I had:
$element = [array]::IndexOf($complist.computer,$compname)
Which did return a value but doesn't seem to be correct (the returned element does not match the correct array element).
The end game is that I need to run through the array for the computers marked as 'Offline', run the scan on that computer and then replace the element in the array with the new data.
Not tested:
$newarray =
foreach ($item in $array)
{
if (
( $item.location -eq 'offline' ) -and
( Test-Connection $Item.computer -Quiet )
)
{
Write-Verbose "Found $($item.Computer) online. Updating."
foreach ($userdir in (Get-ChildItem "\\$($item.Computer)\C$\users" -Directory))
{
$DirSize = (Get-ChildItem $userdir -Recurse | measure -Sum length).sum
[PSCustomobject]#{
Size = $DirSize
Computer = $item.Computer
Location = 'C:\users\' + $userdir.Name
}
}
}
Else { $item }
}
Since it's not a 1:1 replacement, you can't just do a replace - you're going to have to create a new array. This reads in the original array, and creates new entries for computers that were offline but are now online. Everything else just passes through.

Empty value powershell array

I have a strange issue, this is my CSV:
Serveur;Carte;Cordon;IP;Mac;Vmnic ;Vmnic mac;Connect;Port
Dexter;eth1;405;172.16.5.117;00:24:e8:36:36:df;Vmnic0;00:50:56:56:36:df;sw-front-1;A1
Dexter;eth2;14;192.168.140.17;00:24:e8:36:36:e1;Vmnic1;00:50:56:56:36:e1; sw_eq_ds_1;3
;;;;;;;;
Gordon;eth1;404;172.16.5.124;b8:ac:6f:8d:ac:b4;Vmnic0;00:50:56:5d:ac:b4;;
Gordon;eth2;35;192.168.140.114;b8:ac:6f:8d:ac:b6;Vmnic1;00:50:56:5d:ac:b6;;
Gordon;eth3;254;192.168.33.10;b8:ac:6f:8d:ac:b8;Vmnic2;00:50:56:5d:ac:b8;;
So I imported it into an array with the following code:
$Serveur = #()
Import-Csv C:\Users\aasif\Desktop\myfile.csv -Delimiter ";" |`
ForEach-Object {
$Serveur += $_.Serveur
}
And to remove duplicate values I did this :
$Serveur = $Serveur | sort -uniq
So when I display my Array, I obtain these two values : Dexter and Gordon and a third null value
But I also get an empty value
The following code return 3
$Serveur.count
Why?
Thanks for your help
If you want exclude empty values you can do like this
$Serveur = $Serveur | ? { $_ } | sort -uniq
In case someone (like me) needs to remove empty elements from array, but without sorting:
$Serveur = $Serveur | Where-Object { $_ } | Select -Unique
You have an array with 3 elements, so the count is 3. The element you got from the line ;;;;;;;; isn't $null, but an empty string (""), so it counts as a valid element. If you want to omit empty elements from the array, filter them out as C.B. suggested.
On a more general note, I'd recommend against using the += operator. Each operation copies the entire array to a new array, which is bound to perform poorly. It's also completely unnecessary here. Simply echo the value of the field and assign the output as a whole back to a variable:
$csv = 'C:\Users\aasif\Desktop\myfile.csv'
$Serveur = Import-Csv $csv -Delim ';' | % { $_.Serveur } | ? { $_ } | sort -uniq

I can't get PowerShell to cast as int correctly

I am currently importing a CSV file which has a column that is all numbers. I am attempting to cast it as an int and only pull ones that are greater than 100. I have manually gone through this CSV file, and I can confirm that there are three rows with a greater-than-100% value. However, this always returns 0. What am I doing wrong?
$percentTooLarge = Import-Csv path\file.csv | Foreach-Object { $_.SumHoldingPercent = $_.SumHoldingPercent -as [int]; $_ } | Where-Object { $_.SumHoldingPercent -gt 100 } | Measure-Object
$numPercentTooLarge = $percentTooLarge.Count
Because of the way compare operators work in PowerShell, this should do the trick:
$percentTooLarge = Import-Csv path\file.csv |
Where-Object { 100 -lt $_.SumHoldingPercent} |
Measure-Object
Basically, PowerShell, when you compare things, tries to convert right to the type of left. If you put a value from ipcsv first - left will be a string. If you put a numeric first - it will convert the value from the CSV file to a number (it will be smart enough to keep the type big-enough ;))
I tested with this code:
#"
foo,bar,percent
alfa,beta,120.5
beta,gamma,99.9
foo,bar,30.4
works,cool,120.7
"# | ConvertFrom-Csv | where { 100 -lt $_.percent }
... and the results seems OK.
Looking at your conversation with #Shay Levy, I would use:
[System.Globalization.CultureInfo] $culture = "en-us"
So you can try something like :
([decimal]::parse($_.SumHoldingPercent ,$culture)) -gt 100

Resources