PowerShell wildcard in the middle of a string - powershell

I am getting some content from a text file and loading it into a variable.
Once I have the data I iterate through each row of data excluding some values being added into my array like this..
foreach($row in $data)
{
if($row)
{
Write-Host $row
[Array]$index = $row.Split(" ") -ne "[needWildCardHere]" -ne "Value2" -ne "Value3" -ne "Value4" -ne "Value5"
}
}
Each value that matches each string I give, will not be added into the array.
Notice my very first value here [Array]$index = $row.Split(" ") -ne "[needWildCardHere]"
I have a bunch of values in the $row of $data that I have that have a timestamp similar to this:
[10:49:32] [09:32:57] [06:17:15] [06:17:15]
Can I put a wild card inbetween brackets so I do not add any value that has [brackets] into my array?
Feel free to ask me if something is unclear,
Thanks

If you want to use a wildcard match, you need to change from using -ne to -notlike.
If you want to use a regular expression, you'd use -notmatch.
see:
Get-Help about_comparison_operators
An example:
[Array]$index = $row.Split(" ") -notlike "*`[*`]*" -ne "Value2" -ne "Value3" -ne "Value4" -ne "Value5"
Note: the square brackets are considered part of the wildcard set, so to match them literally they need to be escaped with backticks.

Try -notmatch "\[.*\]" instead of -ne

Personally I'd split and then filter since it seems to give more versatile filtering I think:
$Exclusions = #("value1","value2","value3")
gc c:\temp\test.txt |%{$_.split(" ") |?{$_ -notin $exclusions -and $_ -notmatch "\[.*?]" -and !([string]::IsNullOrWhiteSpace($_))}}

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

How to "if" on multiple entries of an array?

I'm wondering if there's a way to simplify the code below by having an if statement that checks if a value matches one of the entries of an array...
The code I ended up with:
foreach ($sourceLine in $source) {
$sourceAVTrim = $sourceLine.ID.Remove(0, 1)
$sourceAV = "av" + $sourceAVTrim
if ($SourceLine.MAC -like "VCO*" -or $SourceLine.MAC -like "FOO*" -or $SourceLine.MAC -like "HOM*" -or $SourceLine.MAC -like "EOP*" -or $SourceLine.MAC -like "PCP*" -or $SourceLine.MAC -like "BUI*" -or $SourceLine.MAC -like "DML*") {
if ($SourceLine.MAC -like "*ADM" -or $SourceLine.MAC -like "*SAL" -or $SourceLine.MAC -like "*PLA" -or $SourceLine.MAC -like "*PLN" -or $SourceLine.MAC -like "*PLC" -or $SourceLine.MAC -like "*PLS") {
Write-Host "$($SourceAV) will NOT receive SMS - $($SourceLine.MAC)" -ForegroundColor Red
} else {
Write-Host "$($SourceAV) will receive SMS - $($SourceLine.MAC)" -ForegroundColor Green
}
} else {
Write-Host "$($SourceAV) will NOT receive SMS - $($SourceLine.MAC)" -ForegroundColor Red
}
}
But how can I set 2 arrays instead and have IF check my value against each entry?
$startMACs = #("VCO","FOO","HOM","EOP","PCP","BUI","DML")
$endMACs = #("ADM","SAL","PLA","PLN","PLC","PLS")
For exact matches you could use the -contains or -in operator. However, in your case you want a partial match against multiple strings, so I'd go with a regular expression.
$startMACs = 'VCO', 'FOO', 'HOM', 'EOP', 'PCP', 'BUI', 'DML'
$pattern = ($startMACs | ForEach-Object { [Regex]::Escape($_) }) -join '|'
if ($SourceLine.MAC -match "^($pattern)") {
...
} else {
...
}
^ anchors the expression at the beginning of a line, so you'll get the matches beginning with those substrings.
To anchor the expression at the end of a line (so you'll get the matches ending with the substrings) replace "^($pattern)" with "($pattern)$".
$pattern is constructed as an alternation (VCO|FOO|HOM|...), meaning "match any of these substrings" ("VCO" or "FOO" or "HOM" or ...).
Escaping the individual terms before building the pattern is so that characters with a special meaning in regular expressions (like for instance . or *) are treated as literal characters. It's not required in this case (since there are no special characters in the sample strings), but it's good practice so that nothing unexpected happens should someone update the list of strings with values that do contain special characters.
Since your patterns all start with a 3 letter code, one possibility is to grab the start of the target string and match using the -in operator with the pattern collection:
$startMACs = #("VCO","FOO","HOM","EOP","PCP","BUI","DML","HDOM")
if($SourceLine.MAC.Substring(0,3) -in $startMACs) {
# Do something
}
So, if $SourceLine.MAC is, say, EOP123, the substring() call grabs the EOP part, then compares it to each entry in the $startMACs array, returning true if it finds a match, and false otherwise - should be true in this example.
You can do something similar for the end patterns:
$endMACs = #("ADM","SAL","PLA","PLN","PLC","PLS")
if($SourceLine.MAC.Substring($SourceLine.MAC.Length - 3,3) -in $endMACs) {
# Do something
}

Powershell: How can you determine which variable in an "if" statement triggered True?

In powershell, I have code that looks like this. The intention is to populate a handful of variables with data if a user doesn't supply any:
if ($1 -eq $null){$1 = "N/A"}
if ($2 -eq $null){$2 = "N/A"}
if ($3 -eq $null){$3 = "N/A"}
Is it possible to condense these down to something like this?
if ($1 -or $2 -or $3 -eq $null){
$FILLER = "N/A"
}
Where $FILLER is the variable(s) that returned True?
Edit: For example, if $2 was null but $1 and $3 were not- then the code would assign N/A to $2.
Note: I don't have a problem with the individual if statements, I'm just aiming to condense repetitive code.
You can use Get-Variable cmdlet. That cmdlet return PSVariable objects, which represent variables, to you. PSVariable objects have Value property, which allows you get and set value of variable. So that, you can filter variables which have particular value ($null), and then assign new value to them.
Get-Variable 1, 2, 3 |
Where-Object { $null -eq $_.Value } |
ForEach-Object { $_.Value = 'N/A' }
You can use ! to define -eq $null or -eq $false:
if (!$1 -or !$2 -or !$3){
$FILLER = "N/A"
}
You could change the order a bit:
$1 = if (!$1) { "N/A" }
But what you're really asking, how to determine which of multiple conditions returned $true in an if statement, is not possible.
If you want to pass an array of values and get back the ones that satisfy a condition, consider Where-Object:
$result = $1,$2,$3 | Where-Object { -not $_ }
If the condition is a string operator like -match or -like it actually works on arrays already and returns an array:
$result = 'apple','apply','ape','zebra' -like 'app*'

I want to check if an element exist in an array

I want to check if a element exist in an array.
$data = "100400296 676100 582"
$i = "18320-my-turn-582"
if ($data -like $i) { Write-Host "Exist" }
else { Write-Host "Didn't exist" }
This example doesn't work like I want it. $i contains 582, so I want it to be Exist in result.
Your string "18320-my-turn-582" doesn't exist in $data, even though both strings contain the substring 582.
PowerShell treats your strings as a whole, and 18320-my-turn-582 is not present in 100400296 676100 582. To work around this you can:
Use Regex:
$i -match '\d+$'
$data -match $Matches[0]
Split the $i at hyphens so you will have:
$i = $i -split '-'
# turns $i into a array with the elements:
# 18320
# my
# turn
# 582
$data -match $i[-1]
# Output: 100400296 676100 582
Check out Get-Help about_Comparison_Operators to understand the differences between -Contains, -Match and -Like operators.

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"
}
}