Powershell Foreach where-object -eq value - powershell

If I import a csv of computer names $computers that looks like this
Host
----
computer5
Computer20
Computer1
and import another csv of data $data that looks like this
Host OS HD Capacity Owner
---- -- -- ------ -----
Computer20 Windows7 C 80 Becky
Computer1 Windows10 C 80 Tom
Computer1 Windows10 D 100 Tom
computer5 Windows8 C 100 sara
computer5 Windows8 D 1000 sara
computer5 Windows8 E 1000 sara
Im trying to do this
foreach ($pc in $computers){
$hostname = $pc.host
foreach ($entry in $data | where {$enty.host -eq $hostname})
{write-host "$entry.Owner"}
}
The foreach statement isn't finding any matches. How can I fix this?
Im trying to do more than just write the owner name but, this is the basic premise
Thanks!
Steve

Replace $enty with $_ inside the Where-Object filter block:
foreach ($entry in $data |Where-Object {$_.host -eq $hostname}){
Write-Host $entry.Owner
}

Here's another way to do it, using an array $computers.host on the left side of the -eq operator:
$computers = 'Host
computer5
Computer20
Computer1' | convertfrom-csv
$data = 'Host,OS,HD,Capacity,Owner
Computer20,Windows7,C,80,Becky
Computer1,Windows10,C,80,Tom
Computer1,Windows10,D,100,Tom
computer5,Windows8,C,100,sara
computer5,Windows8,D,1000,sara
computer5,Windows8,E,1000,sara' | convertfrom-csv
$data | where { $computers.host -eq $_.host } | ft
Host OS HD Capacity Owner
---- -- -- -------- -----
Computer20 Windows7 C 80 Becky
Computer1 Windows10 C 80 Tom
Computer1 Windows10 D 100 Tom
computer5 Windows8 C 100 sara
computer5 Windows8 D 1000 sara
computer5 Windows8 E 1000 sara
Or
$data | where host -in $computers.host
Or
foreach ( $entry in $data | where host -in $computers.host | foreach owner ) {
$entry }
Becky
Tom
Tom
sara
sara
sara
Assuming $entry is spelled correctly, this syntax just happens not to work.
The expression $data | where {$entry.host -eq $hostname} just doesn't make sense by itself. Good question though. You can indeed use a pipeline in the parens after foreach.
foreach ($pc in $computers){
$hostname = $pc.host
foreach ($entry in $data | where {write-host "entry is $entry";
$entry.host -eq $hostname}){
write-host "$entry.Owner"
}
}
entry is
entry is
entry is
entry is
entry is
entry is
entry is
entry is
entry is
entry is
entry is
entry is
entry is
entry is
entry is
entry is
entry is
entry is

Related

Compare multiple elements in an object against multiple elements in another object of a different array

Say [hypothetically], I have two .CSVs I'm comparing to try and see which of my current members are original members... I wrote a nested ForEach-Object comparing every $name and $memberNumber from each object against every other object. It works fine, but is taking way to long, especially since each CSV has 10s of thousands of objects. Is there another way I should approach this?
Original_Members.csv
Name, Member_Number
Alice, 1234
Jim , 4567
Current_Members.csv
Alice, 4599
Jim, 4567
$currentMembers = import-csv $home\Desktop\current_members.csv |
ForEach-Object {
$name = $_.Name
$memNum = $_."Member Number"
$ogMembers = import-csv $home\Desktop\original_members.csv" |
ForEach-Object {
If ($ogMembers.Name -eq $name -and $ogMembers."Member Number" -eq $memNum) {
$ogMember = "Yes"
}
Else {
$ogMember = "No"
}
}
[pscustomobject]#{
"Name"=$name
"Member Number"=$memNum
"Original Member?"=$ogMember
}
} |
select "Name","Member Number","Original Member?" |
Export-CSV "$home\Desktop\OG_Compare_$(get-date -uformat "%d%b%Y").csv" -Append -NoTypeInformation
Assuming both of your files are like the below:
Original_Members.csv
Name, Member_Number
Alice, 1234
Jim, 4567
Current_Members.csv
Name, Member_Number
Alice, 4599
Jim, 4567
You could store the original member names in a System.Collections.Generic.HashSet<T> for constant time lookups, instead of doing a linear search for each name. We can use System.Linq.Enumerable.ToHashSet to create a hashset of string[] names.
We can then use Where-Object to filter current names by checking if the hashset contains the original name with System.Collections.Generic.HashSet<T>.Contains(T), which is an O(1) method.
$originalMembers = Import-Csv -Path .\Original_Members.csv
$currentMembers = Import-Csv -Path .\Current_Members.csv
$originalMembersLookup = [Linq.Enumerable]::ToHashSet(
[string[]]$originalMembers.Name,
[StringComparer]::CurrentCultureIgnoreCase
)
$currentMembers |
Where-Object {$originalMembersLookup.Contains($_.Name)}
Which will output the current members that were original members:
Name Member_Number
---- -------------
Alice 4599
Jim 4567
Update
As requested in the comments, If we want to check both Name and Member_Number, we can concatenate both strings to use for lookups:
$originalMembers = Import-Csv -Path .\Original_Members.csv
$currentMembers = Import-Csv -Path .\Current_Members.csv
$originalMembersLookup = [Linq.Enumerable]::ToHashSet(
[string[]]($originalMembers |
ForEach-Object {
$_.Name + $_.Member_Number
}),
[StringComparer]::CurrentCultureIgnoreCase
)
$currentMembers |
Where-Object {$originalMembersLookup.Contains($_.Name + $_.Member_Number)}
Which will now only return:
Name Member_Number
---- -------------
Jim 4567

Get-ADComputer from Multiple Computers from a CSVFile

I have a csv file full of computer information formatted:
Name OS Site Code AD_Status Region Tech
computerone Windows 10 Enterprise **** Exists Chicago T T
computertwo Windows 10 Enterprise **** Exists Chicago T T
computerthree Windows 10 Enterprise **** Exists Chicago T T
I'm running a Powershell script that grabs the computer name from the csv file and checks its 'modifyTimeStamp' field.
$csvfile = Import-CSV -Path 'C:\Users\****\testexcel.csv'-Delimiter ","
$numofcompsincsv = $csvfile.psobject.properties.value[0] - 1
for ($i = 0; $i -le $numofcompsincsv; $i++) {
Get-ADComputer -identity $csvfile[$i].psobject.properties.value[0] -Properties * | FT Name, modifyTimeStamp
}
The problem with this is that it prints the computer information one by one, for example:
Name modifyTimeStamp
---- ---------------
computerone 7/19/2019 11:06:22 AM
Name modifyTimeStamp
---- ---------------
computertwo 7/24/2019 6:02:14 AM
Name modifyTimeStamp
---- ---------------
computerthree 7/24/2019 2:02:14 AM
How can I modify this so that it prints all in one like:
Name modifyTimeStamp
---- ---------------
computerone 7/19/2019 11:06:22 AM
computertwo 7/24/2019 6:02:14 AM
computerthree 7/24/2019 2:02:14 AM
Don't do this with a for loop; use the pipe instead:
Import-CSV -Path 'C:\Users\****\testexcel.csv' | Select -expand Name | Get-ADComputer -prop modifyTimeStamp | Select Name,ModifyTimeStamp
ETA: I apparently didn't create the CSV (hand-coded) I used for testing quite properly, and had originally piped the CSV to | Select Name | instead of expanding the property (which worked in my test). When I re-did it using Excel, I verified that | Select -expand Name | was required, per the comment by #js2010.

Update CSV column using Powershell

I have CSV1 (as below) where I have to populate the last column from CSV 2 given below..
CSV1
Computer Product Code Country Department LaptopDesktop
-------- ------- ---- ------- ---------- -------------
Com1 EliteDesk 705 HP2190 AU FN
Com2 EliteBook 830 HP1023 AU IT
Com3 EliteBook 830 HP1023 FR FN
Com4 Zbook 15U HP2020 IN FN
Com5 OptiPlex 3010 DL1721 FR FN
CSV2
Product Type Code
------- ---- ----
EliteBook 1030 L HP1020
EliteBook 1040 L HP1021
EliteBook 830 L HP1023
Zbook 15U L HP2020
EliteDesk 800 D HP3035
EliteDesk 705 D HP2190
Thinkpad L480 L LE990
OptiPlex 3010 D DL1721
with below code, I'm unable to update 'LaptopDesktop' column in CSV1 whereas output gets appended with -Append parameter or without that, it just overwrite the whole CSV.....How do I fix this?
{
$SystemData =#()
$Productinfo = Import-Csv "C:\CSV2.csv"
$Mastercsv = Import-Csv "C:\CSV1.csv"
foreach($record in $Mastercsv)
{
$mcode = $($record.code)
$mDLtype = $($record.LaptopDesktop)
$SysType = ($Productinfo | where {$_.code -eq $mcode}).type
if ($SysType -eq $null)
{
$sysType = 'Unknown, due to non-matching code'
}
$AddsysType=New-Object PSCustomObject
Add-Member -InputObject $AddsysType -membertype noteproperty -name "LaptopDesktop" -value $sysType
$SystemData +=$AddsysType
}Return, $SystemData
}
$DeviceType= Update-SystemType
$DeviceType| Export-Csv 'C:\CSV1.csv' -Force -NoTypeInformation
I think the fact you've just opened the file to import then try to export to it again is confusing PowerShell. In any case, it's probably not a good idea to over-write the file in this way as it could lead to corruption. Probably better to write to a separate CSV like this:
function Update-SystemType {
Import-Csv ".\CSV2.csv" | ForEach-Object {$codeHash = #{}}{
$codeHash[$_.Code] = $_
}{}
Import-Csv ".\CSV1.csv" |
ForEach-Object {
$_.LaptopDesktop = $(if($codeHash.ContainsKey($_.Code)){$codeHash[$_.Code].Type}else{"Unknown"})
$_
}
}
Update-SystemType |
Export-Csv '.\CSV3.csv' -Force -NoTypeInformation
If you really want to replace the original afterwards, then you can over-write it like this:
Move-Item -Path .\CSV3.csv -Destination .\CSV1.csv -Force
When posting csv sample data, either use the real comma delimited format -or-
the columnar representation after Import-Csv.
## Q:\TEst\2019\01\17\SO_54236818.ps1
$Mastercsv = Import-Csv ".\CSV1.csv"
$Productinfo = Import-Csv ".\CSV2.csv"
foreach($record in $Mastercsv){
$record.LaptopDesktop = ($Productinfo|Where-Object Code -eq $record.code).Type
}
$Mastercsv | Format-Table -Auto
Sample output
Computer Product Code Country Department LaptopDesktop
-------- ------- ---- ------- ---------- -------------
Com1 EliteDesk 705 HP2190 AU FN D
Com2 EliteBook 830 HP1023 AU IT L
Com3 EliteBook 830 HP1023 FR FN L
Com4 Zbook 15U HP2020 IN FN L
Com5 OptiPlex 3010 DL1721 FR FN D
Com6 unknown na DE IT

PowerShell display duplicates in CSV

I have a csv with headers like this
ID,Name,IP,Details
There are some value will be duplicated like this
1,John,192.168.1.1,Details1
2,Mary,192.168.1.2,Details2
3,John,192.168.1.3,Details3
4,Dick,192.168.1.1,Details4
5,Kent,192.168.1.4,Details5
Is there anyway I can select all lines with duplicated values?
Desired Output:
1,John,192.168.1.1,Details1
3,John,192.168.1.3,Details3
4,Dick,192.168.1.1,Details4
So far I have tried
Import-csv file | group | sort -des | select -f 10
but the result only group those with whole lines matching
I will be appreciate if anyone could lend a hand or show direction for me to solve this question. Thanks in advance for any reply
I'm not sure if I've understood your problem up to 100%, but as far as I've understood you want something like this:
# ID Name IP Detail
#-- ---- -- ------
#1 John 192.168.1.1 Details1
#2 Mary 192.168.1.2 Details2
#3 John 192.168.1.3 Details3
#4 Dick 192.168.1.1 Details4
#5 Kent 192.168.1.4 Details5
$input = Import-csv .\input.csv
$input
$uniqueNames = $input.Name | select -Unique
$duplicateNames = Compare-Object -ReferenceObject $input.Name -DifferenceObject $uniqueNames | ? { $_.SideIndicator -like "<=" } | select -ExpandProperty InputObject
$uniqueIps = $input.IP | select -Unique
$duplicateIps = Compare-Object -ReferenceObject $input.IP -DifferenceObject $uniqueIps | ? { $_.SideIndicator -like "<=" } | select -ExpandProperty InputObject
Write-Output ""
Write-Output "========================="
Write-Output "Duplicate Names: $duplicateNames"
Write-Output "Duplicate IPs: $duplicateIps"
Output:
ID Name IP Detail
-- ---- -- ------
1 John 192.168.1.1 Details1
2 Mary 192.168.1.2 Details2
3 John 192.168.1.3 Details3
4 Dick 192.168.1.1 Details4
5 Kent 192.168.1.4 Details5
=========================
Duplicate Names: John
Duplicate Names: 192.168.1.1
Hope that helps.

Getting value from CSV

I'm writing a PowerShell script to read a CSV. I have everything working so far, it's able to find the value from user input (branch number) and finds the values that have "Y" in the available. Please see 2nd picture.
First picture is my CSV file.
This is what I need help with. How would I get the value of the first available CoreID? In this example, FMD354800000. Once I get the first available CoreId, I want to change the Available to N
$Find = $ImportCSV | Select-String -Pattern $GetBranchNum
$Find -match "Y"
New-ItemProperty -path $CoreIP -name "TTable ID" -PropertyType String -Value "Test" -Force
Use Import-Csv to import the CSV:
$csv = Import-Csv 'C:\path\to\your.csv'
Since the file seems to be fixed width you'll need to Trim() fields before checking their value.
$branchNumber = '8000'
$first = $csv | Where-Object {
$_.'Branch Number'.Trim() -eq $branchNumber -and
$_.Available.Trim() -eq 'y'
} | Select-Object -First 1
This filters the CSV for records with the given branch number that have a value Y in the field Available and selects the first matching record.
Then change the value of the Available field of that record:
$first.Available = 'N'
Demonstration:
PS C:\> $csv = Import-Csv 'C:\path\to\sample.csv'
PS C:\> $csv
Branch Number CoreID Available
------------- ------ ---------
8000 FMD354800000 Y
8000 FMD354800001 Y
8000 FMD354800002 N
PS C:\> $first = $csv | Where-Object {
>> $_.'Branch Number'.Trim() -eq '8000' -and
>> $_.Available.Trim() -eq 'y'
>> } | Select-Object -First 1
>>
PS C:\> $first
Branch Number CoreID Available
------------- ------ ---------
8000 FMD354800000 Y
PS C:\> $first.Available = 'N'
PS C:\> $first
Branch Number CoreID Available
------------- ------ ---------
8000 FMD354800000 N
PS C:\> $csv
Branch Number CoreID Available
------------- ------ ---------
8000 FMD354800000 N
8000 FMD354800001 Y
8000 FMD354800002 N
Neither Select-String nor the -match operator are particularly useful in this context, so don't use them.