在 powershell 中每 6 个管道更换一次

Ten*_*ore 6 powershell regex string-manipulation

我意识到我在问一个已经被问过和回答过的类似问题,但我无法推断出我需要的答案,因为正则表达式和正则表达式引擎足够不同。我有硬件资产管理日志,它们以管道分隔,但不是端点之间的主要分隔。日志如下所示:

|STATUS1|HOSTNAME1|IP1|MAC1|IS_WIRED1|STATUS2|HOSTNAME2|IP2|MAC2|IS_WIRED2|STATUS3|HOSTNAME3|IP3|MAC3|IS_WIRED3
Run Code Online (Sandbox Code Playgroud)

我想要做的是用|回车替换每 6 次,看起来像这样:

|STATUS1|HOSTNAME1|IP1|MAC1|IS_WIRED1
|STATUS2|HOSTNAME2|IP2|MAC2|IS_WIRED2
|STATUS3|HOSTNAME3|IP3|MAC3|IS_WIRED3
Run Code Online (Sandbox Code Playgroud)

我得到的最接近的选择每个端点,但我不太确定如何使用 powershell 来利用它。

[^\|]*\|[^\|]*\|[^\|]*\|[^\|]*\|[^\|]*\|[^\|]*
Run Code Online (Sandbox Code Playgroud)

我熟悉 PS 中的替换命令,我想象最终结果会是这样的:

$hosts = $hosts -replace "<highspeed_low_drag_velcro_snap_regex_here>","\r\n"
Run Code Online (Sandbox Code Playgroud)

提前致谢!

Bob*_*Bob 8

好的,所以这个实际上有点棘手。可以说,正则表达式不是这项工作的最佳工具,但它可以做到。

-replace "(?<=^((\|[^|]*){5})+)\|","`n|"
Run Code Online (Sandbox Code Playgroud)

我会试着引导你完成它:

  • 您的文本有一个要匹配的部分和一个要替换的部分。传统上,正则表达式会替换整个搜索字符串,因此您可以使用捕获组来指定要克隆到替换输出的搜索字符串的某些部分。另一种方法是使用环视,这就是我在这里所做的。PowerShell (.NET) 是少数支持可变长度lookbehinds的正则表达式语言之一,所以我们很幸运。
  • (?<=)部分是回顾。这意味着一切的=)匹配的,但不是取代。So^((\|[^|]*){5})+用作条件- 仅当此位与预期替换之前的文本匹配时才会发生替换。
  • ^((\|[^|]*){5})*[^|]*部分可以概括为“从行首 ( ^),匹配 5 个|s 的集合,然后将文本匹配到下一个|”。
    • 行的开头^很重要 - 否则它可以匹配行中的任何地方,并且无法保证|之前有多少个s。
    • 因为|在正则表达式中有特殊含义,需要转义:\|. 在字符类 ( []) 中时不需要对其进行转义。
    • [^|]*意思是“直到下一个文本|”——更专业地说,“尽可能多的字符|”——更技术地说“尽可能多地重复[^|]字符类,其中该字符类匹配除|“以外的任何字符。
    • * 表示“前一个字符的零次或多次重复,尽可能多”
    • 所以(\|[^|]*)意味着 match|后跟尽可能多的字符,直到下一个|. 这将匹配|text
    • {5}表示将前面的标记准确重复 5 次。它完全等同于将前面的标记复制粘贴 5 次。所以这将匹配|text|text|text|text|text
    • ((\|[^|]*){5})+是整个组的一次或多次重复。所以它可以匹配|text|text|text|text|text|text|text|text|text|text|text|text|text|text|text等 - 以 5 的倍数。我们使用+代替 的原因*是我们不想匹配空组并替换第一个|
    • 这使得整个lookbehind,这意味着它只会用它后面|的5 |s的倍数替换a ,从行的开头开始。
  • 接下来是 a\|作为要替换的实际文本,前面是匹配的lookbehind。
  • 以您的示例为例|STATUS1|HOSTNAME1|IP1|MAC1|IS_WIRED1|STATUS2|HOSTNAME2|IP2|MAC2|IS_WIRED2|STATUS3|HOSTNAME3|IP3|MAC3|IS_WIRED3,它将匹配以下内容:

    |STATUS1|HOSTNAME1|IP1|MAC1|IS_WIRED1**|**STATUS2|HOSTNAME2|IP2|MAC2|IS_WIRED2**|**STATUS3|HOSTNAME3|IP3|MAC3|IS_WIRED3
    
    Run Code Online (Sandbox Code Playgroud)

您会在此处注意到(如果您还没有),您实际上是在尝试每5th |减去第一个而不是每6th替换一次。但是lookbehind方法相当干净地处理“减去第一个”的情况。


现在是替换字符串。

  • 因为这是 PowerShell,所以当我们想要 时\n,我们实际上想要,`n因为 PowerShell 转义字符是`. 请注意,这仅在替换字符串中是必需的;在正则表达式本身中,您仍将使用\n该文字序列传递给正则表达式引擎。
  • 因为你|在每一行都有一个前导,我们需要|在新行之后添加一个新的。这是可行的,因为您的原始行不以 a 结尾|,因此在行尾没有任何可替换的内容,因此我们不会以额外的新行结束,也不会以 结尾|

如果您更喜欢更传统的捕获组方法:

-replace "((?:[^|]+\|){4}[^|]+)\|","`$1`n|"
Run Code Online (Sandbox Code Playgroud)

弄清楚这是如何工作的留给读者作为练习;) 提示:$1反向引用必须被转义(使用`),否则 PowerShell 会将其解释为 shell 变量。