Powershell - Matching text from array and string? - powershell

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

Related

Why Isn't This Counting Correctly | PowerShell

Right now, I have a CSV file which contains 3,800+ records. This file contains a list of server names, followed by an abbreviation stating if the server is a Windows server, Linux server, etc. The file also contains comments or documentation, where each line starts with "#", stating it is a comment. What I have so far is as follows.
$file = Get-Content .\allsystems.csv
$arraysplit = #()
$arrayfinal = #()
[int]$windows = 0
foreach ($thing in $file){
if ($thing.StartsWith("#")) {
continue
}
else {
$arraysplit = $thing.Split(":")
$arrayfinal = #($arraysplit[0], $arraysplit[1])
}
}
foreach ($item in $arrayfinal){
if ($item[1] -contains 'NT'){
$windows++
}
else {
continue
}
}
$windows
The goal of this script is to count the total number of Windows servers. My issue is that the first "foreach" block works fine, but the second one results in "$Windows" being 0. I'm honestly not sure why this isn't working. Two example lines of data are as follows:
example:LNX
example2:NT
if the goal is to count the windows servers, why do you need the array?
can't you just say something like
foreach ($thing in $file)
{
if ($thing -notmatch "^#" -and $thing -match "NT") { $windows++ }
}
$arrayfinal = #($arraysplit[0], $arraysplit[1])
This replaces the array for every run.
Changing it to += gave another issue. It simply appended each individual element. I used this post's info to fix it, sort of forcing a 2d array: How to create array of arrays in powershell?.
$file = Get-Content .\allsystems.csv
$arraysplit = #()
$arrayfinal = #()
[int]$windows = 0
foreach ($thing in $file){
if ($thing.StartsWith("#")) {
continue
}
else {
$arraysplit = $thing.Split(":")
$arrayfinal += ,$arraysplit
}
}
foreach ($item in $arrayfinal){
if ($item[1] -contains 'NT'){
$windows++
}
else {
continue
}
}
$windows
1
I also changed the file around and added more instances of both NT and other random garbage. Seems it works fine.
I'd avoid making another ForEach loop for bumping count occurrences. Your $arrayfinal also rewrites everytime, so I used ArrayList.
$file = Get-Content "E:\Code\PS\myPS\2018\Jun\12\allSystems.csv"
$arrayFinal = New-Object System.Collections.ArrayList($null)
foreach ($thing in $file){
if ($thing.StartsWith("#")) {
continue
}
else {
$arraysplit = $thing -split ":"
if($arraysplit[1] -match "NT" -or $arraysplit[1] -match "Windows")
{
$arrayfinal.Add($arraysplit[1]) | Out-Null
}
}
}
Write-Host "Entries with 'NT' or 'Windows' $($arrayFinal.Count)"
I'm not sure if you want to keep 'Example', 'example2'... so I have skipped adding them to arrayfinal, assuming the goal is to count "NT" or "Windows" occurrances
The goal of this script is to count the total number of Windows servers.
I'd suggest the easy way: using cmdlets built for this.
$csv = Get-Content -Path .\file.csv |
Where-Object { -not $_.StartsWith('#') } |
ConvertFrom-Csv
#($csv.servertype).Where({ $_.Equals('NT') }).Count
# Compatibility mode:
# ($csv.servertype | Where-Object { $_.Equals('NT') }).Count
Replace servertype and 'NT' with whatever that header/value is called.

Powershell find value in arraylist

I hope you can help me out. I work with two ArrayLists:
array1 is filled with log.csv (contains header and logon-data, values of column 'pc' are unique). It's defined as ArrayList because I want to add entries.
#pc,name,date,city
#pc-x,name-1,2017-01-01,berlin
#pc-y,name-1,2017-01-02,berlin
#pc-z,name-2,2017-01-02,munich
[System.Collections.ArrayList]$array1 = Import-Csv log.csv
array2 is filled during runtime
$array2=[System.Collections.ArrayList]#()
... ForEach-Object {
$array2.Add([PSCustomObject]#{ pc=$pcname
name=$loginname
date=$logindate }) >$null }
What I want to do:
Update array1.date=array2.date where array1.pc=array2.pc
If no entry found in array1 I want to add it:
$array1.Add([PSCustomObject]#{ pc=$pcname
name=$loginname
date=$logindate
city='[unknown]' }) >$null
Finally array1 is exported again:
$array1 | Export-Csv log.csv -Encoding UTF8 -NoTypeInformation
So the question is: how can I find the entry in array1 and update it? Trying hard for days now ...
try Something like this:
$array1=import-csv "C:\temp\log.csv"
$array2=import-csv "C:\temp\log2.csv"
#modify founded and output not founded
$toadd=$array2 | %{
$current=$_
$founded=$array1 | where pc -eq $current.pc | %{$_.date=$current.date;$_}
if ($founded -eq $null)
{
$current.city='UNKNOW'
$current
}
}
#output of $array1 modified and elements to add
$array1, $toadd
Here is a sample I created that might help. Note: I use List types instead of ArrayList ones. Also, it assumes only one possible matching PC name in the data to be updated. You'll have to alter it to update the file since it merely updates the first List variable. Let me know how it goes.
[PSCustomObject]
{
[string] $pc,
[string] $name,
[string] $date,
[string] $city
}
[System.Collections.Generic.List[PSCustomObject]] $list1 = Import-Csv "C:\SOSamples\log.csv";
[System.Collections.Generic.List[PSCustomObject]] $list2 = Import-Csv "C:\SOSamples\log2.csv";
[PSCustomObject] $record = $null;
[PSCustomObject] $match = $null;
foreach($record in $list2)
{
# NOTE: This only retrieves the FIRST MATCHING item using a CASE-INSENSITIVE comparison
$match = $list1 | Where { $_.pc.ToLower() -eq $record.pc.ToLower() } | Select -First 1;
if($match -eq $null)
{
Write-Host "Not Found!";
$list1.Add($record);
}
else
{
Write-Host "Found!";
$match.date = $record.date;
}
}
Write-Host "--------------------------------------------------------------------"
foreach($record in $list1)
{
Write-Host $record
}

Retrieving second part of a line when first part matches exactly

I used the below steps to retrieve a string from file
$variable = 'abc#yahoo.com'
$test = $variable.split('#')[0];
$file = Get-Content C:\Temp\file1.txt | Where-Object { $_.Contains($test) }
$postPipePortion = $file | Foreach-Object {$_.Substring($_.IndexOf("|") + 1)}
This results in all lines that contain $test as a substring. I just want the result to contain only the lines that exactly matches $test.
For example, If a file contains
abc_def|hf#23$
abc|ohgvtre
I just want the text ohgvtre
If I understand the question correctly you probably want to use Import-Csv instead of Get-Content:
Import-Csv 'C:\Temp\file1.txt' -Delimiter '|' -Header 'foo', 'bar' |
Where-Object { $_.foo -eq $test } |
Select-Object -Expand bar
To address the exact matching, you should be testing for equality (-eq) rather than substring (.Contains()). Also, there is no need to parse the data multiple times. Here is your code, rewritten to to operate in one pass over the data using the -split operator.
$variable = 'abc#yahoo.com'
$test = $variable.split('#')[0];
$postPipePortion = (
# Iterate once over the lines in file1.txt
Get-Content C:\Temp\file1.txt | foreach {
# Split the string, keeping both parts in separate variables.
# Note the backslash - the argument to the -split operator is a regex
$first, $second = ($_ -split '\|')
# When the first half matches, output the second half.
if ($first -eq $test) {
$second
}
}
)

Check if a string contains any substring in an array in PowerShell

I am studying PowerShell. I want to know how to check if a string contains any substring in an array in PowerShell. I know how to do the same in Python. The code is given below:
any(substring in string for substring in substring_list)
Is there similar code available in PowerShell?
My PowerShell code is given below.
$a = #('one', 'two', 'three')
$s = "one is first"
I want to validate $s with $a. If any string in $a is present in $s then return True. Is it possible in PowerShell?
Using the actual variables in the question for simplicity:
$a = #('one', 'two', 'three')
$s = "one is first"
$null -ne ($a | ? { $s -match $_ }) # Returns $true
Modifying $s to not include anything in $a:
$s = "something else entirely"
$null -ne ($a | ? { $s -match $_ }) # Returns $false
(That's about 25% fewer characters than chingNotCHing's answer, using the same variable names of course :-)
($substring_list | %{$string.contains($_)}) -contains $true
should strictly follow your one-liner
For PowerShell ver. 5.0+
Instead of,
$null -ne ($a | ? { $s -match $_ })
try this simpler version:
$q = "Sun"
$p = "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
[bool]($p -match $q)
This returns $True if substring $q is in the array of string $p.
Another Example:
if ($p -match $q) {
Write-Host "Match on Sun !"
}
Michael Sorens' code answer works best to avoid the pitfall of partial substrings matching. It just needs a slight regex modification. If you have the string $s = "oner is first", the code would still return true since 'one' would match 'oner' (a match in PowerShell means the second string contains the first string.
$a = #('one', 'two', 'three')
$s = "oner is first"
$null -ne ($a | ? { $s -match $_ }) # Returns $true
Add some regex for word boundary '\b' and the r on 'oner' will now return false:
$null -ne ($a | ? { $s -match "\b$($_)\b" }) # Returns $false
(I know it's an older thread but at least I might help people looking at this in the future.)
Any response given that uses -match will produce incorrect answers.
Example: $a -match $b will produce false negatives if $b is "."
A better answer would be to use .Contains - but it's case sensitive so you'd have to set all strings to upper or lower case before comparing:
$a = #('one', 'two', 'three')
$s = "one is first"
$a | ForEach-Object {If ($s.toLower().Contains($_.toLower())) {$True}}
Returns $True
$a = #('one', 'two', 'three')
$s = "x is first"
$a | ForEach-Object {If ($s.toLower().Contains($_.toLower())) {$True}}
Returns nothing
You could tweak it to return $True or $False if you'd want, but IMO the above is easier.
I'm amazed that in 6 years nobody has given this more simple and readable answer
$a = #("one","two","three")
$s = "one1 is first"
($s -match ($a -join '|')) #return True
So simply implode the array into a string using vertical bar "|" , as this is the alternation (the "OR" operator) in regex.
https://www.regular-expressions.info/alternation.html
https://blog.robertelder.org/regular-expression-alternation/
Also keep in mind that the accepted answer will not search for exact match. If you want exact match you can use the \b (word boundary)
https://www.regular-expressions.info/wordboundaries.html
$a = #("one","two","three")
$s = "one1 is first"
($s -match '\b('+($a -join '|')+')\b') #return False
It is possible to select a subset of strings containing any of the strings like this:
$array = #("a", "b")
$source = #("aqw", "brt", "cow")
$source | where {
$found = $FALSE
foreach($arr in $array){
if($_.Contains($arr)){
$found = $TRUE
}
if($found -eq $TRUE){
break
}
}
$found
}
One way to do this:
$array = #("test", "one")
$str = "oneortwo"
$array|foreach {
if ($str -match $_) {
echo "$_ is a substring of $str"
}
}

Powershell foreach over two arrays

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.