Powershell foreach over two arrays - powershell

All,
Trying to get this working but have not had any luck with ps.
$a =
"install.res.1028.dll"
"install.res.1031.dll"
"install.res.1033.dll"
"install.res.1036.dll"
"install.res.1040.dll"
"install.res.1041.dll"
"install.res.1042.dll"
"install.res.2052.dll"
$b =
"install.res.1041.dll"
"install.res.1042.dll"
"install.res.2052.dll"
I just wish to have a new array with the values that are found in $a and not found in $b, trying to test it out with write-host but no luck.
I have tried compare-object but I am unable to pull out just the name. I am a totally noob with ps.
Please, any suggestions appreciated.
foreach ($i in $a)
{ foreach-object ($b | where { {$_.name} -ne $i }) { write-host $i}}

Another solution using compare-object as follows:
$c = Compare-Object -ReferenceObject $a -DifferenceObject $b -PassThru
output:
install.res.1028.dll
install.res.1031.dll
install.res.1033.dll
install.res.1036.dll
install.res.1040.dll
CORRECTION:
The above code WILL work for the case where DifferenceObject is guaranteed to be a subset of ReferenceObject. It will FAIL, though, if there are additional objects in DifferenceObject that are not also present in ReferenceObject. The above code returns any objects which are present in EITHER ReferenceObject OR DifferenceObject but NOT in both.
To properly return ONLY objects in ReferenceObject that are not also present in DifferenceObject, the following code is required:
Compare-Object -ReferenceObject $a -DifferenceObject $b |
Where-Object { $_.SideIndicator -eq '<=' } |
ForEach-Object { Write-Output $_.InputObject }
The where-object clause ensures only objects that are present in ReferenceObject are passed down the pipeline.
The foreach-object clause forces the output back to a simple array (ref: Converting Custom Object arrays to String arrays in Powershell - thanks Keith)

$c = $a | Where-Object { $b -notcontains $_ }
This should do the job.
Some Explanation
Where-Object's code block tests each element of the array that's piped into it. The block is supposed to return a boolean value, and if it's true, then the result of the call will contain the item in question.
So for the conditional, we use the -notcontains operator with your second array. $_ refers to the individual item from $a.
This doesn't require an additional or nested loop.

Related

Powershell - Matching text from array and string?

I have an array that simply pulls a list of numbers in one long column. I am trying to match it with some data in a string and if it matches, I am wanting it to state Down otherwise it will state Up in the output CSV.
Here is my code: `
IF($RESULTS -like $TEST)
{$Output = "DOWN"
}ELSE{
$OUtput = "UP"
}
`
$RESULTS is the array, and $TEST is the string. If I do -match it works, but -match only pulls single digits so it gives false positives. For example, if there is a 3 in the list as well as 638 it will mark them both as down. None of the other switches seem to work like -eq, -like, etc.
What am I missing please?
Thanks much for any assistance!
EDIT:
Sample of Data in $TEST
2
3
5
Sample of Output of $RESULTS
5
628
Since 5 exists in both, my expected output would be DOWN and everything else would be UP.
It sounds like you have two arrays of numbers, and you want to test if the input array contains at least one of the values in the test array.
You can use Compare-Object, which compares two arrays and indicates which elements are different and, with -IncludeEqual, also which ones are the same:
if (
(Compare-Object -IncludeEqual $RESULTS $TEST).
Where({ $_.SideIndicator -eq '==' }).
Count -gt 0
) {
$Output = "DOWN"
}
else {
$Output = "UP"
}
As an aside:
You can use an if statement as an expression, which means you only need to specify $Output once:
$Output =
IF (<# conditional #>) {
"DOWN"
}
ELSE {
"UP"
}
In PowerShell (Core) 7+, you can use ?:, the ternary conditional operator for a more concise solution:
$Output = <# conditional #> ? 'DOWN' : 'UP'
I would do it by using "foreach". Hope this might be helpful
foreach ($result in $RESULTS){
if ($result -like $Test){
$OUTPUT = "Down"}
else{
$OUTPUT= "UP"}
}
In your edited question you show that variable $TEST is also an array, so in that case you can do
$TEST = 2,3,5
$RESULTS = 5,628
# compare both arrays for equal items
$Output = if ($TEST | Where-Object { $RESULTS -contains $_ }) {"DOWN"} else {"UP"}
$Output
In this case, $Output will be DOWN because both arrays have the number 5
If however variable $TEST contains a multiline string, then first create an array out of that like
$TEST = #'
2
3
5
'#
# convert the string to array by splitting at the newline
$TEST = $TEST -split '\r?\n' -ne ''
$RESULTS = 5,628
# compare both arrays for equal items
$Output = if ($TEST | Where-Object { $RESULTS -contains $_ }) {"DOWN"} else {"UP"}
$Output

Find lines in one Object based on strings in other object

Finding lines in one file based on rows in another file.
I have one object $A with some rows like:
0c7d3283-bec2-4db1-9078-ebb79d21afdf
200bc957-26dd-4e8e-aa6e-00dc357c4ac2
218e0d2a-0e8b-4a68-8136-8f5dd749a614
I want to find matches in object $B for those rows and print the lines with the matches to an output file.
I've been trying for a week now (my first week in powershell :) )
I've come to:
$F = $B | ForEach-Object{ $A | Select-String -Pattern $_$ -AllMatches| Select-Object line }
but this doesn't give me any returning results.
Who is willing to help me out?
If you want to match your first Array, with something that should match part of a string in a second array you do something like the code below:
$A = #("0c7d3283-bec2-4db1-9078-ebb79d21afdf", "200bc957-26dd-4e8e-aa6e-00dc357c4ac2", "218e0d2a-0e8b-4a68-8136-8f5dd749a614")
$B = #("Something 0c7d3283-bec2-4db1-9078-ebb79d21afdf", "Something else 200bc957-26dd-4e8e-aa6e-00dc357c4ac2", "Something also e3df3978-beb7-4545-bc48-ff40d8453be1")
foreach ($Line in $A) {
if($B -match $Line) {
$B | Where-Object {$_ -match $Line}
}
}
We first loop through all the lines in the first object, then compare if the line is matched to anything in the second array. If we find a match we go through Array B to find where the Line from A matches.
You could make this code a hell lot prettier, but this the most understandable way I can write it.
Old Answer
You could use the Compare-Object cmdlet to compare the two arrays, then use the -IncludeEqual switch to show where there are matches and then use the -ExcludeDifferent switch to remove the results that do not match.
Then take that Output and put in in a file. A simple test could be something like this:
$A = #("0c7d3283-bec2-4db1-9078-ebb79d21afdf", "200bc957-26dd-4e8e-aa6e-00dc357c4ac2", "218e0d2a-0e8b-4a68-8136-8f5dd749a614")
$B = #("0c7d3283-bec2-4db1-9078-ebb79d21afdf", "200bc957-26dd-4e8e-aa6e-00dc357c4ac2", "e3df3978-beb7-4545-bc48-ff40d8453be1")
(Compare-Object -ReferenceObject $A -DifferenceObject $B -ExcludeDifferent -IncludeEqual).InputObject | Out-File .\output.txt
This should output a file in your Shells current working directory with the two GUIDs that match:
0c7d3283-bec2-4db1-9078-ebb79d21afdf
200bc957-26dd-4e8e-aa6e-00dc357c4ac2
Where the one that didn't match is not included.

Powershell- compare files and take action

I'm attempting to create a powershell script to compare two IP addresses out of two different text files then take an action base on if they match or they don't match. See below
(Invoke-WebRequest ifconfig.me/ip).content | Out-File "C:\test\test2.txt"
$ErrorActionPreference = "Stop"
$File = "C:\test\test.txt"
$File2 = "C:\test\test2.txt"
Compare-Object -ReferenceObject (Get-Content $File2) -DifferenceObject (Get-
Content $File) -IncludeEqual
If the two files match I obviously get a ==, or an => if they don't match. I'm not sure how to use == or => as a variable to continue the rest of the script. Any help or recommendations are greatly appreciated.
One way is to check the value of the SideIndicator property; e.g.:
if ( (Compare-Object (Get-Content test1.txt) (Get-Content test2.txt) -IncludeEqual).SideIndicator -eq '==' ) {
"The files are equal"
}
Compare-Object outputs nothing if there are no differences and you omit -IncludeEqual, so the above can also be written as:
if ( -not (Compare-Object (Get-Content test1.txt) (Get-Content test2.txt)) ) {
"The files are equal"
}
You can assign a variable to the Compare-Object result. When I did a quick test it seems like nothing is returned if the two objects matched. So you would have to check for $null in the value of the variable.
$a = Compare-Object -ReferenceObject "abc" -DifferenceObject "abc"
if($a -ne $null){continue working here}else{do other action here}
For more info check out:
https://ss64.com/ps/compare-object.html

Use of where-object

I would like to use Where-Object to simplify my code and make use of powershell function.
Consider these 2 lists
$list = #('A','B','C')
$list2=#{'A'=1;'B'=2;'E'=3}
What I would like to do is find out in $list all the items that are keys in $list2. I can do this using the normal trivial way.
$r = #()
foreach ($t in $list)
{
$t
if ($list2.ContainsKey($t))
{
$r+=$t
}
}
The code above works but when I issue the command below
$r = $list | Where-Object $list2.Keys.contains($_)
Powershell was not happy and said the method was not supported. I think it should be achievable but I do not know how to issue the proper command. Please help if this is doable as my script seems to be too wordy.
You will need to wrap your the conditional that you are evaluating in your Where-Object in {} to evaluate as a ScriptBlock. Secondly, the contains() method will return a boolean for each member of $list2 so you will want to evaluate that statement for if the returned array contains the value $True.
$r = $list | Where-Object {($list2.Keys.contains($_)) -contains $True}
Alternatively, you could use the .contains() method on the $list2 hashtable itself and avoid getting a value for each key.
$r = $list | Where-Object {$list2.contains($_)}

For-Each aggregation on a single HashtBable returns an array of HashTable[]

I'd like to scan the src/ directory for all .pdb files but deduplicate them by their names (since a file of the same name might appear multiple times in various subdirectories). I thought something like this would do it.
$x = ls .\src\ -Recurse -Include *.pdb |
% { $a[$_.Name] = $_; $a } -Begin { $a = #{} }
And it sort of does but surprisingly $x.GetType() is Object[] not Hashtable. Instead, each of the items in the Object[] array seems to be a Hashtable.
I really don't understand why this is happening, it seems like $x should be a Hashtable
This is happening because you are passing back $a once for each file. The ForEach-Object loop has 3 parts, the -Begin, -Process, and -End. The -Begin and -End parts happen once, and the -Process scriptblock happens once for each item passed to it.
What you need to do is move where you pass $a from the -Process block to the -End block. This way you create the empty hashtable in -Begin, you populate it in -Process, and then—once you have everything in it—you pass it along in -End.
$x = ls .\src\ -Recurse -Include *.pdb |
% -Begin { $a = #{} } -Process { $a[$_.Name] = $_ } -End { $a }
I think, I got the thing. Let me know if it helps your understanding.
While iterating you are basically initiating $a.
If you see $a.GetType(), its a hashtable with a basetype as an Object
So, $x.getType() is actually holding an array of basetype System.Objects
That's why you are getting Object Array in $x inside which each one is an Object type Hashtable (as $a).
Hope it helps you.