我意识到我问了一个类似的问题,这个问题已经被问过并回答过了,但我无法推断出我需要的答案,因为正则表达式和正则表达式引擎有足够的不同。我有硬件资产管理日志,它们是管道分隔的,但不是端点之间的主要分隔。日志如下所示:
|STATUS1|HOSTNAME1|IP1|MAC1|IS_WIRED1|STATUS2|HOSTNAME2|IP2|MAC2|IS_WIRED2|STATUS3|HOSTNAME3|IP3|MAC3|IS_WIRED3
我想要做的是将每 6 个|
回车符替换为一个,如下所示:
|STATUS1|HOSTNAME1|IP1|MAC1|IS_WIRED1
|STATUS2|HOSTNAME2|IP2|MAC2|IS_WIRED2
|STATUS3|HOSTNAME3|IP3|MAC3|IS_WIRED3
我最接近的选择每个端点,但我不太确定如何使用 powershell 来利用它。
[^\|]*\|[^\|]*\|[^\|]*\|[^\|]*\|[^\|]*\|[^\|]*
我熟悉 PS 中的替换命令,我想象最终结果将是这样的:
$hosts = $hosts -replace "<highspeed_low_drag_velcro_snap_regex_here>","\r\n"
提前致谢!
答案1
好吧,这实际上有点棘手。可以说,正则表达式不是完成这项工作的最佳工具,但它可以完成这项工作。
-replace "(?<=^((\|[^|]*){5})+)\|","`n|"
我将尝试引导你完成它:
- 您的文本中有部分内容您想要匹配以及您想要的部分代替。传统上,正则表达式会替换整个搜索字符串,因此您可以使用捕获组指定要克隆到替换输出的搜索字符串的某个部分。另一种方法是使用环视四周,这就是我在这里所做的。PowerShell (.NET) 是少数支持可变长度后视,所以我们很幸运。
- 此
(?<=)
部分是回顾。这意味着=
和之间的所有内容)
都是匹配但不是被取代.因此^((\|[^|]*){5})+
被用作状况- 仅当此位与预期替换之前的文本匹配时,才会发生替换。 - 该
^((\|[^|]*){5})*[^|]*
部分可以概括为“从行首(^
)开始,匹配五个|
s 的集合,然后匹配文本直到下一个 s|
”。- 行的开头
^
很重要 - 否则它可能会匹配行中的任何位置,并且无法保证|
之前有多少个 s。 - 因为
|
在正则表达式中具有特殊含义,所以需要进行转义:\|
。在字符类中时无需进行转义([]
)。 [^|]*
表示“文本到下一个” — 更技术性地说,“尽可能|
多的除 之外的字符” — 更技术性地说,“尽可能多地重复字符类,其中该字符类与除 之外的任何字符匹配。|
[^|]
|
*
表示“尽可能重复前一个字符零次或多次”- 所以
(\|[^|]*)
意味着匹配|
尽可能多的字符,直到下一个|
。这将匹配|text
{5}
表示重复前一个标记 5 次。这完全等同于复制粘贴前一个标记 5 次。因此这将匹配|text|text|text|text|text
((\|[^|]*){5})+
是整个组的一个或多个重复。因此它可以匹配|text|text|text|text|text
、等——以 5 的倍数。我们使用而不是的|text|text|text|text|text|text|text|text|text|text
原因是我们不想匹配空组并替换第一个。+
*
|
- 这就完成了整个后视,意味着它只会从行首开始
|
用恰好 5 的倍数来替换 a 。|
- 行的开头
- 接下来是作为
\|
要替换的实际文本,前面是匹配的后视文本。 以您的例子来说
|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
你会在这里注意到(如果你还没有注意到的话),你实际上是在尝试替换每一个第五 |
除第一个之外,不是每一个第六。但是后视方法可以相当干净地处理“减去第一个”的情况。
现在是替换字符串。
- 因为这是 PowerShell,所以当我们需要 时
\n
,我们实际上想要的是`n
因为 PowerShell 转义字符是`
。请注意,这仅在替换字符串中是必要的;在正则表达式本身中,您仍将使用\n
该文字序列将该文字序列传递给正则表达式引擎。 - 因为每行都有一个前导
|
,所以我们需要|
在新行后添加一个新行。这样做是可行的,因为您的原始行不以 结尾|
,因此行尾没有任何内容需要替换,因此我们不会得到额外的新行或尾随|
。
如果您更喜欢更传统的捕获组方法:
-replace "((?:[^|]+\|){4}[^|]+)\|","`$1`n|"
弄清楚这是如何工作的就留给读者练习了;)提示:$1
反向引用必须进行转义(使用`
),否则 PowerShell 会将其解释为 shell 变量。