1

We have an application that keeps a server database - a list of server names and other related information. Sometimes we need to export the information in the XML format to process it by a Powershell script. Server names in the XML file can be in simple ("ServerXX") or FQDN ("ServerXX.abc.com") formats. The script searches for a server name that is always in the simple format, and the search results should contain all simple and full server names that match the searched name.

The main search operator (slightly simplified) looks like this:

$FoundServer = ($ServerList | Where {$_.Name -match $ServerName+"*"})

$ServerList here is the array of strings (server names). Looks simple and works as expected. Usually.

The strange thing is, sometimes the script can't find some FQDNs. For example, if the FQDN in the file is "ServerXX.abc.com", and we're searching for "ServerXX", the FQDN is not found. At the same time search for other names works as expected. When debugging the script, it can be seen that the expression inside {} is literally "ServerXX.abc.com" -like "ServerXX*". It MUST be true. But the resulting search result is empty. And even more interesting thing is, if the search name is specified as "ServerXX.", "ServerXX.a" or with other letters from the FQDN, the script finds it. If the same server name is specified in the file without the domain name (in the simple form), the script finds it.

Well, and even more enigmatic thing is, we have two instances of the installed application, one for production, another one for testing. The test one contains a much smaller server database. If I add the "invisible" server name from the prod instance to the test one and export the database, the script finds this name without any problems.

If I replace -like with -match, the issue disappears. So it's not an issue of the XML file generator (it's another PS script that generates a PSCustomObject and exports it via Export-CliXml). It's also not an issue of some invisible or non-ANSI symbols in the server name. I also examined the content of the XML file manually. It's huge (several tens of megabytes) and complex, so it's pretty difficult to analyze but I didn't find any visible issue. The XML structure looks correct.

I don't understand that random behavior. Can it be related somehow to the XML file size? Memory lack in PS or something like that? We use Powershell v4.

mklement0
  • 382,024
  • 64
  • 607
  • 775
Evgeny
  • 43
  • 1
  • 8
  • Try: `$FoundServer = ($ServerList | Where-Object {$_.Name -match $('{0}.*' -f [regex]::Escape($Servername))})` – Theo Mar 04 '19 at 21:45
  • 1
    There's not enough information to diagnose your problem; the description of what you're doing and your code snippets do not explain the behavior. Perhaps if you give an example of a server name that produces the symptom we can help (it doesn't have to be a real server name, as long as it produces the symptom). – mklement0 Mar 04 '19 at 22:00
  • @Theo: Good idea, but you should anchor the regex (`^`), escape the `.` as `\.`, and use `(...)`, not `$(...)` (the latter is inefficient and only needed if you need to embed _multiple_ statements). – mklement0 Mar 04 '19 at 23:01
  • 1
    @mklement0 Good points, however, the `.` was intended as 'any character except newline'. In either case, it would not really make a difference, because the `*` makes the dot valid for zero or more occurrences. – Theo Mar 05 '19 at 10:10
  • Hi all. Regarding the -like and -match operators, there is a real need for -like. This is an additional safety measure to ensure that the script always looks for the specified server name in the beginning of server names. Among other things, the script automatically deletes server names from the database, and I want to exclude the scenario where the operator finds the substring in the middle of a wrong server name. It can result in deletion of a wrong server. The string concatenation in the example is really done earlier in the code, I placed it in the example code for illustrative purposes. – Evgeny Mar 05 '19 at 17:00
  • I'm not good with Regex expressions. That sample - "$FoundServer = ($ServerList | Where-Object {$_.Name -match $('{0}.*' -f [regex]::Escape($Servername))})" - is it fully equal to -like "substring*"? – Evgeny Mar 05 '19 at 17:02
  • AN example of the wrong server name is "servername.domain1.domain2.com". The problem is, the script can find some such FQDNs by "servername" and can't find others looking exactly the same. – Evgeny Mar 05 '19 at 17:04

1 Answers1

1

Note that this answer is not a solution, because (as of this writing) there's not enough information to diagnose your problem; however, your use of the -like and -match operators deserves some scrutiny.


$_.Name -match $ServerName+"*" (more succinctly: $_.Name -match "$ServerName*") is not the same as $_.Name -like "$ServerName*":

  • -match uses regular expressions (regexes), which (also) match part of the input, unless explicitly formulated to match at the start (^) and/or the end ($) of the input.

  • -like uses wildcard expressions, which must match the input as a whole.

While regexes and wildcards are distantly related, their syntax - and capabilities - are different; regexes are far more powerful; in the case at hand (note that matching is case-insensitive by default):

  • ... -like 'ServerXX*' matches a string that starts with ServerXX and is followed by zero or more arbitrary characters (*).

    • Inputs 'ServerXX', 'ServerXX.foo.bar' and 'ServerXXY' would all return $true.
  • ... -match 'ServerXX*' matches a string that contains substring ServerX (just one X!) anywhere in the input, if followed by zero or more (*) X characters, because duplication symbol * modifies the preceding character/subexpression.

    • While inputs 'ServerXX' and 'ServerXX.foo.bar' would return $true, so would 'ServerX' and 'fooServerXX' - which is undesired in this case.

If your inputs are FQDNs, use either of the following expressions, which are equivalent:

... -like 'ServerXX.*'

... -match '^ServerXX\.'

If the server name is supplied via variable, e.g. $ServerName, use "...", an expandable string, in the simplest case:

... -like "$ServerName.*"

... -match "^$ServerName\."

This is fine in the case of server names, as they're not permitted to contain characters that could mistakenly be interpreted as regex / wildcard metacharacters (characters with special meaning, such as *).

Generally, the safest approach is to explicitly escape a variable value to ensure its literal use, though note that needing to do so is much more likely in a regex than in a wildcard expression, because regexes have many more metacharacters:

... -like ('{0}.*' -f [System.Management.Automation.WildcardPattern]::Escape($ServerName))


... -match ('^{0}\.' -f [regex]::Escape($ServerName))

Using a single-quoted template string with -f, the format operator ({0} represents the 1st RHS operand), makes it obvious which parts are used literally, and which parts are spliced in as an escaped variable value.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 1
    Hi mklement0. Thanks, basically my problem is resolved by using -match "^$ServerName" in the expression. I'll also keep in mind variable escaping, I wasn't aware about this feature. Not needed right now but looks useful in general. Special symbols in the search variable were also my concern. I still don't understand the erratic behavior of the -like operator but oh well. Doesn't worth investigating. The entire script is long and heavily depends on using application-specific PS calls, so putting it here in full doesn't make sense. It's Ivanti Protect, if you know what it is. Thanks again. – Evgeny Mar 05 '19 at 19:33