(PowerShell)拆分带有转义分隔符的字符串

时间:2014-02-14 14:53:22

标签: powershell split escaping character

拆分模块通常用于将Active Directory可分辨名称和规范名称拆分为RDN,方便地忘记可能在OU和CN中使用的转义分隔符:

带有转义逗号的

专有名称示例:

CN=Test User,OU=Comma\,Test,OU=Test,DC=domain,DC=com

带有转义斜杠的规范名称示例:

Domain.com/Test/Slash\/Test/Test User

互联网上有几个分裂的例子甚至没有提到这个可能长时间工作的陷阱,但迟早会导致很多麻烦来解决这个编程缺陷。

我认为没有一种简单的方法可以使用正则表达式正确分割转义字符串(另请参阅:是否存在包含转义序列的字符串的纯正则表达式分割?)。

4 个答案:

答案 0 :(得分:4)

使用负面观察:

$text = 'CN=Test User,OU=Comma\,Test,OU=Test,DC=domain,DC=com'
$text -split '(?<!\\),'

CN=Test User
OU=Comma\,Test
OU=Test
DC=domain
DC=com

$text = 'Domain.com/Test/Slash\/Test/Test User'
$text -split '(?<!\\)/'

Domain.com
Test
Slash\/Test
Test User

答案 1 :(得分:1)

我认为仍然存在一个小陷阱,因为RND可能以反斜杠结束(将通过额外的反斜杠进行转义):

$text = 'CN=Test User,OU=EndSlash\\,OU=Comma\,Test,DC=domain,DC=com'
$text -split '(?<!\\),'
CN=Test User
OU=EndSlash\\,OU=Comma\,Test
DC=domain
DC=com

换句话说,只有在前面有奇数个反斜杠时才应跳过相关的分隔符。 为了解决这个问题,我认为完整的正则表达式应该是: (?<![^\\](\\\\)*\\),(代表尊敬的名字)和 (?<![^\\](\\\\)*\\)/(对于规范名称)。

$text = 'CN=Test User,OU=EndSlash\\,OU=Comma\,Test,DC=domain,DC=com'
$text -split '(?<![^\\](\\\\)*\\),'
CN=Test User
OU=EndSlash\\
OU=Comma\,Test
DC=domain
DC=com

$text = 'Domain.com/Slash\/Test/EndSlash\\/Test/Test User'
$text -split '(?<![^\\](\\\\)*\\)/'
Domain.com
Slash\/Test
EndSlash\\
Test
Test User

答案 2 :(得分:1)

总结并补充现有的,有用的答案:

  • mjolinor's answer如果您不必担心\\作为转义\ 出现在输入中,效果会很好。
    如果存在\\,解决方案会误解,中的\\, 转义(而不是转义\后跟未转义的, 1}})。

  • iRon's own answer使用更复杂的正则表达式解决了这个问题

此外,您可能希望在分割后 删除转义字符 ;构建在Wiktor Stribiżew here提供的正则表达式上,并使用正则表达式-replace添加\\(.)操作:

PS> 'foo,bar\,baz,bang\\,last' -split '(?<=(?<!\\)(?:\\\\)*),' -replace '\\(.)', '$1'
foo
bar,baz
bang\
last

这是一个简单实用程序函数,它包含上面的内容,带有可配置的分隔符和转义字符。:

function Split-Text {
  param(
      [Parameter(Mandatory=$True)] [string] $Text,
      [Parameter(Mandatory=$True)] [string] $Separator,
      [string] $EscapeChar = '\'
  )
  $Text -split
      ('(?<=(?<!{0})(?:{0}{0})*){1}' -f [regex]::Escape($EscapeChar), [regex]::Escape($Separator)) `
          -replace ('{0}(.)' -f [regex]::Escape($EscapeChar)), '$1'
}
# Sample call - yields the same as above.
Split-Text 'foo,bar\,baz,bang\\,last' ','

# With "/" as the separator - analogous output.
Split-Text 'foo/bar\/baz/bang\\/last' '/'

答案 3 :(得分:0)

因此,我创建了一个小cmdlet,为现有的拆分模块添加了一个转义功能:

Function Split {
    param(
        [Parameter(Mandatory = $True, ValueFromPipeline = $true)][String]$String,
        [Parameter(Mandatory = $False, Position = 0)][String]$Delimiter = " ",
        [Parameter(Mandatory = $False, Position = 1)][Int]$MaxSubstrings = 0,
        [Parameter(Mandatory = $False, Position = 2)][String]$Escape,
        [Parameter(Mandatory = $False, Position = 3)][String]$Options = ""
    )
    If ($Escape) {$String = $String.Replace("$Escape$Delimiter", [String][Char]27)}
    $Split = $String -Split $Delimiter, $MaxSubstrings, $Options
    If ($Escape) {$Split | ForEach {$_.Replace([String][Char]27, "$Escape$Delimiter")}} Else {$Split}
}

"CN=Test User,OU=Comma\,Test,OU=Test,DC=domain,DC=com" | Split ","  -Escape "\"
"Domain.com/Test/Slash\/Test/Test User" | Split "/" -Escape "\"