使用包含带通配符的条件的数组的问题

Issue using an array containing criteria with wildcards

我一直在尝试 运行 一个 powershell 代码来将一个文件分成 2 个。

我有几个常规数组,效果很好。 第三个数组包含每个条件的通配符,这根本不起作用。

我试过 -in -notin、-like -notlike、-contains -notcontains、-match -notmatch,但没有得到我想要的结果。

    $NonAutoStructure = @("Not_Found", "UK Training Centre", "IRISH Training Centre", "Head Office", "UK Newmedica")
$AutoJournalDescriptions = @("STORE TRANFrom *",  "*SALES BANKED*")#, "*/* CREDIT" , "BANKING DIFF*BQ*" , "*/* MASTERCARD/VISA")  
$InactiveStores = @("4410", "0996", "1015", "5996")


$NonAutoJournalCompanies = {$_.Description -notcontains $AutoJournalDescriptions} 
$AutoJournalCompanies = {$_.Description -contains $AutoJournalDescriptions}
#$NonAutoJournalCompanies = {$_.structure -in $NonAutoStructure -or $_.Company -in $InactiveStores -and  $_.Amount -ne "0.00"}
#$AutoJournalCompanies = {$_.structure -notin $NonAutoStructure-and $_.Company -notin $InactiveStores -and  $_.Amount -ne "0.00"}

$UNREC_S0 | Where-Object $NonAutoJournalCompanies | Export-Csv \774512-LRBSPT01\*****$\uardata\rt1\BankRec\Test\step1\TestNonAutoJournal.txt -notype
$UNREC_S0 | Where-Object $AutoJournalCompanies | Export-Csv \774512-LRBSPT01\*****$\uardata\rt1\BankRec\Test\step1\TestAutoJournal.txt -notype
$UNREC_S0 | Where-Object $ZeroValuelines | Export-Csv \774512-LRBSPT01\*****$\uardata\rt1\BankRec\Test\step1\TestZeroLines.txt -notype

我遇到问题的数组是 $AutoJournalDescriptions。如果数组包含单个条件,我只能让它工作。否则,它似乎会忽略它们。 这里它只包含一对,但# 之后的标准也应该包括在内。 我试图将这些条件作为#(Non)AutojournalCompanies 文件的一部分包括和排除,以便所有数据都被保留,但被分开,然后可以被定向到不同的流程流。

也许我只是想使用一个不适合那样工作的函数...? 我整天都在寻找解决方案,但无济于事。 我可以在文件生成标准中单独键入所有这些标准,但这使得阅读起来很繁琐,维护起来也很麻烦。当需要更改时,我更愿意 enrich/modify 数组。

我希望一切都有意义。 我是 powershell 的新手。

非常感谢,

安托万

  • 为了匹配 通配符 模式(例如 *SALES BANKED*),您需要 -like 运算符;相比之下,-contains 执行 equality 比较(针对每个数组元素隐式 -eq)。

  • 虽然这些运算符(连同其他运算符,例如 -eq-match)支持 input 值的数组[1]比较操作数(通常为 RHS)必须是 标量 (单个值) - 您不能同时将输入数组与 多个 值进行比较。


在您的场景中,最好的办法是使用 regexes (regular expressions) 而不是通配符表达式,并将它们组合成一个 single 带有交替运算符 (|) 的正则表达式,因此您可以使用单个 -match 操作来测试多个模式:

# Sample input
$UNREC_S0  = [pscustomobject] @{ Description = 'A SALES BANKED baz' }, 
             [pscustomobject] @{ Description = 'bar' }, 
             [pscustomobject] @{ Description = 'STORE TRANFrom foo' }, 
             [pscustomobject] @{ Description = 'unrelated' }

# The filtering criteria: *regexes* to match against the descriptions,
# combined into a single regex with the alternation operator, '|'
$AutoJournalDescriptions = '^STORE TRANFrom ', 'SALES BANKED' -join '|'

# Construct script blocks to use with `Where-Object` below.
$NonAutoJournalCompanies = { $_.Description -notmatch $AutoJournalDescriptions } 
$AutoJournalCompanies =    { $_.Description -match $AutoJournalDescriptions}

$UNREC_S0 | Where-Object $NonAutoJournalCompanies | Export-Csv \774512-LRBSPT01\*****$\uardata\rt1\BankRec\Test\step1\TestNonAutoJournal.txt -notype
# ...

以上生成了以下 CSV 数据,显示仅导出了与正则表达式匹配的 not 描述:

"Description"
"bar"
"unrelated"

请注意正则表达式 ^STORE TRANFrom 如何对应通配符表达式 STORE TRANFrom *,以及 SALES BANKED*SALES BANKED*.

通配符 * 运算符 - 通常对应于正则表达式中的 .* - 在此处的正则表达式中不需要,因为 -match 运算符隐式执行 substring 匹配(而使用 -like 的通配符匹配匹配 整个 输入字符串)。


可选阅读:通过子字符串或模式数组过滤字符串值数组:

如果您将 条件表述为 regexes(正则表达式),则可以使用 Select-String cmdlet,其中 是否支持多个比较操作数:

# Sample input
$descriptions = 'A SALES BANKED baz', 'bar', 'STORE TRANFrom foo', 'unrelated'

# The filtering criteria: *regexes* to match against the descriptions.
$descriptionRegexes = '^STORE TRANFrom ', 'SALES BANKED'

($descriptions | Select-String -Pattern $descriptionRegexes).Line

注意:您可以也可以使用此技术查找文字子串,方法是使用-SimpleMatch 而不是 -Pattern,但请注意,子字符串随后会在每个输入字符串中的任何位置 进行匹配,而不能将匹配限制在例如字符串的开头字符串。

以上输出如下(一个 2 元素数组):

A SALES BANKED baz
STORE TRANFrom foo

您可以使用类似的方法,通过将各个正则表达式组合到一个带有交替 (|) 运算符[=144= 的正则表达式中],其中 允许使用 -match 运算符:

# Sample input
$descriptions = 'A SALES BANKED baz', 'bar', 'STORE TRANFrom foo', 'unrelated'

# The filtering criteria: *regexes* to match against the descriptions,
# combined into a single regex with the alternation operator, '|'
$descriptionRegex = '^STORE TRANFrom ', 'SALES BANKED' -join '|'
# -> '^STORE TRANFrom |SALES BANKED'

$descriptions -match $descriptionRegex

您还可以将此方法应用于文字子串匹配,即通过转义在正则表达式中使用 [regex]::Escape() 的子字符串;例如,
$descriptionRegex = ('yes?', '2.0').ForEach({ [regex]::Escape($_) }) -join '|'


否则,如果你确实需要通配符支持,你将不得不——效率低下——嵌套循环(如果您可以做出具体假设,请参阅下面的快捷方式):

# Sample input
$descriptions = 'A SALES BANKED baz', 'bar', 'STORE TRANFrom foo', 'unrelated' 

# The filtering criteria: wildcard patterns to match against the descriptions.
$descriptionWildcards = 'STORE TRANFrom *', '*SALES BANKED*'

foreach ($descr in $descriptions) {
  foreach ($wildcard in $descriptionWildcards) {
    if ($descr -like $wildcard) { $descr; break }
  }
}

请注意,我使用了 foreach statements 而不是带有 ForEach-Object cmdlet 调用的管道;前者更快,后者可以在输入 streamed 时保持内存消耗不变;对于已经在内存中的数组,foreach 语句是更好的选择。


你可以走捷径,如果你能做出两个假设:

  • 没有一个通配符模式匹配多个输入。

  • 输入顺序不需要保留;也就是说,描述的输出顺序反映通配符模式数组中条目的顺序而不是输入描述的顺序是可以接受的。

# Sample input
$descriptions = 'A SALES BANKED baz', 'bar', 'STORE TRANFrom foo', 'unrelated' 

# The filtering criteria: wildcard patterns to match against the descriptions.
$descriptionWildcards = 'STORE TRANFrom *', '*SALES BANKED*'

# Loop over the criteria and match the descriptions against each.
# `foreach` is the built-in alias for the `ForEach-Object` cmdlet.
# The output order will be reflect the order of the wildcard patterns.
$descriptionWildcards | foreach { $descriptions -like $_ }

在这种情况下,虽然生成的元素相同,但它们的顺序不同:

STORE TRANFrom foo
A SALES BANKED baz

[1] 将一组值作为输入,这些运算符的作用类似于 过滤器:也就是说,它们 return 匹配值的子数组;例如,1, 2, 3 -eq 2 returns 2 作为单元素数组。