powershell -match string with 如果它具有某个字符之一。即lastname_firstname不匹配lastname_firstname_middleinitail

powershell -match string with if it has one of a certain character. I.E lastname_firstname not match lastname_firstname_middleinitail

提问人:Warlord213 提问时间:5/6/2023 最后编辑:Santiago SquarzonWarlord213 更新时间:5/6/2023 访问量:124

问:

我正在尝试匹配只有一个下划线的字符串。Match 目前正在寻找lastname_firstname和lastname_firstname_middleinitail。我只想返回lastname_firstname并排除其他所有内容。

我试过有同样的问题。-match "._." ".(_)." "._{1}" -like "*_*"

这是我的代码中具有 -match 的部分

$newUsers = Get-ADUser -SearchBase "CN=Users,DC=atlantic,DC=county" -Filter {WhenCreated -ge $date7DaysAgo} | Where-Object {$_.name -match "._."}
正则表达式 PowerShell Active-Directory 匹配

评论


答:

0赞 Cid 5/6/2023 #1

Where-Object {$_.name -match "_.*?_"}将检查输入字符串中是否有 2 个或更多下划线,但您想要相反的下划线。因此,您可以使用 NOT 运算符反转输出:$_.name -match "_.*?_"

Where-Object { !($_.name -match "_.*?_") }

评论

0赞 mklement0 5/6/2023
请注意,PowerShell 否定了运算符变体:.值得一提的是:这个问题在这方面没有明确说明,但可以想象,其目的是仅将字符串与一个字符串匹配,在这种情况下,您还必须排除那些没有字符串的字符串。$_.name -notmatch '_.*?_'_
1赞 Santiago Squarzon 5/6/2023 #2

我建议您使用 LDAP 执行部分过滤以减少数据集,从而提高代码效率。 将只搜索其属性至少包含一个的用户,不包括那些没有属性的用户。(name=*_*)name_

对于第二层过滤,^[a-z]+_[a-z]+$ 将确保名称只有一个,但也只有 to 的字符,如果您需要更具包容性,即名称可能必须 ,您可以使用 。_az09^[a-z0-9]+_[a-z0-9]+$

$daysAgo = [datetime]::UtcNow.AddDays(-7).ToString('yyyyMMddHHmmss.0Z')

$getADUserSplat = @{
    SearchBase = 'CN=Users,DC=atlantic,DC=county'
    LDAPFilter = "(&(WhenCreated>=$daysAgo)(name=*_*))"
}

$newUsers = Get-ADUser @getADUserSplat |
    Where-Object { $_.Name -match '^[a-z]+_[a-z]+$' }

评论

0赞 Warlord213 5/6/2023
谢谢 '^[a-z]+_[a-z]+$ 工作得很好。使用 [datetime]::UtcNow.AddDays(-7) 有什么好处吗?ToString('yyyyMMddHHmmss.0Z')。在我的代码中,我使用 $days = “7” # 获取 $days 天前的日期 $date 7DaysAgo = $(get-date)。AddDays(-$days)
0赞 Santiago Squarzon 5/6/2023
@Warlord213没有优势,这就是 AD 提供商在幕后将过滤器转换为幕后的方式。问题是,使用您当前拥有的过滤器,您将无法同时添加 ,它会引发错误。因此,我需要将过滤器转换为LDAP语法,以便AD模块不会抛出错误;){WhenCreated -ge $date7DaysAgo}name -like '*_*'
1赞 Warlord213 5/6/2023
我明白你说什么了。我曾尝试使用 -like 看看它是否会产生不同的输出,但我现在只使用 -match。您还帮助我了解 + 和 ?需要与 A range 结合使用。这就是以前绊倒我的原因。感谢您的帮助。
0赞 Santiago Squarzon 5/6/2023
@Warlord213供参考,如果您想使用代替 ,语法将与我在这里所做的相同,应该具有该格式。FilterLDAPFilter"whenCreated -ge '$daysAgo' -and name -like '*_*'"$daysAgoyyyyMMddHHmmss.0Z
1赞 mklement0 5/6/2023 #3

让我补充圣地亚哥的有用答案,这是解决您特定问题的最佳方法,并回答您的问题标题所暗示的一般问题:

对于 N >= 0,如何测试字符串是否恰好包含给定字符的 N 次出现

$char = '_'
$n = 2
@(
  'no underscore',
  'one_underscore',
  'two_underscores_here'
  'three_underscores_are_here'
) | 
  Where-Object {
    # Matches only 'two_underscores_here'
    ($_ -replace "[^$char]").Length -eq $n
  }

注意:

  • -replace 使用正则表达式实际上删除了除感兴趣字符以外的所有字符,仅保留由感兴趣字符的实例组成的字符串,其长度意味着出现次数。 "[^$char]"

  • 对于最多 N 个/小于 N 个和至少 N个/大于 N 个逻辑,请替换为 /-eq-le-lt-ge / -gt


或者,直接调用 [regex]::Matches() .NET API:

$char = '_'
$n = 2
@(
  'no underscore',
  'one_underscore',
  'two_underscores_here'
  'three_underscores_are_here'
) | 
  Where-Object {
    # Matches only 'two_underscores_here'
    ([regex]::Matches($_, "[$char]")).Count -eq $n
  }

为了完整起见:如果 N 等于 1 或 N 是固定的,或者您需要至少 N 个逻辑,并且您不介意更复杂的正则表达式增加复杂性,您还可以按如下方式使用 -match

  • 为简单起见,在以下示例中,对感兴趣的字符进行了硬编码-
  • 为了更容易适应其他角色,即使对于正匹配,它也被包含在其中,即使这不是绝对必要的。[...]_
@(
  'no underscore',
  'one_underscore',
  'two_underscores_here'
  'three_underscores_are_here'
) | 
  Where-Object {
    # Matches only 'one_underscore'
    $_ -match '^[^_]*[_][^_]*$'
  }

具有固定 N 值> 1 的示例;请注意,正则表达式部分所需的显式重复(反向引用在一定程度上减轻了这一点)使得这对于较大的 N 值是不切实际的:\1

# N = 2
@(
  'no underscore',
  'one_underscore',
  'two_underscores_here'
  'three_underscores_are_here'
) | 
  Where-Object {
    # Matches only 'two_underscores_here'
    $_ -match '^[^_]*([_])[^_]*\1[^_]*$'
  }

At-least-N 逻辑大大简化了正则表达式:

# N >= 2
@(
  'no underscore',
  'one_underscore',
  'two_underscores_here'
  'three_underscores_are_here'
) | 
  Where-Object {
    # Matches 'two_underscores_here' and 'three_underscores_are_here'
    $_ -match '_.*_'
  }

N 是 0 的边缘情况最容易处理,因为您需要做的就是确保不存在,这很容易做到:_-notmatch

# N = 0
@(
  'no underscore',
  'one_underscore',
  'two_underscores_here'
  'three_underscores_are_here'
) | 
  Where-Object {
    # Matches only 'no underscore'
    $_ -notmatch '[_]'
  }