Powershell 如何比较文件中的两个连续字符串

Powershell 如何比较文件中的两个连续字符串,powershell,Powershell,我有一个大文件,包括每个项目的“之前”和“之后”案例,如下所示: case1 (BEF) ACT (AFT) BLK case2 (BEF) ACT (AFT) ACT case3 (BEF) ACT (AFT) CLC ... 我需要选择所有在“第一个”字符串上有(BEF)ACT,在“第二个”字符串上有(AFT)BLK,并将结果放入文件中 我们的想法是创建一个如下的子句 IF (stringX.LineNumber consists of "(BEF) A

我有一个大文件,包括每个项目的“之前”和“之后”案例,如下所示:

case1 (BEF) ACT
      (AFT) BLK
case2 (BEF) ACT
      (AFT) ACT
case3 (BEF) ACT
      (AFT) CLC
...
我需要选择所有在“第一个”字符串上有
(BEF)ACT
,在“第二个”字符串上有
(AFT)BLK
,并将结果放入文件中

我们的想法是创建一个如下的子句

IF (stringX.LineNumber consists of "(BEF) ACT" AND stringX+1.LineNumber consists of (AFT) BLK)
{OutFile $stringX+$stringX+1}
抱歉,语法错误,我刚刚开始使用PS:)

创建一个新文件,结果如下: 带“(BEF)ACT”的字符串,后跟带“(AFT)BLK”的字符串

  • -SimpleMatch
    执行字符串文字子字符串匹配,这意味着您可以按原样传递搜索字符串,而无需转义它

    • 但是,如果需要进一步限制搜索,例如确保搜索只发生在一行的末尾(
      $
      ),则确实需要使用(隐含的)
      -Pattern
      参数:
      '\(BEF\)ACT$'

    • 另请注意,PowerShell默认情况下通常不区分大小写,这就是为什么使用开关
      -CaseSensitive

  • 注意如何直接接受文件路径-无需前面的
    Get Content
    调用

  • -Context 0,1
    捕获每次匹配之前的
    0
    行和之后的
    1
    行,并将它们包含在
    Select String
    输出的实例中

  • ForEach对象
    脚本块中,
    $\u0.Context.PostContext[0]
    检索匹配后的行,并
    .Contains()
    在其中执行文字子字符串搜索

    • 请注意,
      .Contains()
      是.NET
      系统.String
      类型的方法,与PowerShell不同,这些方法在默认情况下区分大小写,但您可以使用可选参数来更改
  • 如果在后续行中找到子字符串,则同时输出当前行和后续行

  • 上面查找输入文件中的所有匹配对;如果您只想找到第一对,请将
    | Select Object-first 2
    附加到
    Select String
    调用中


另一种方法是将$logFile作为单个字符串读入,并使用正则表达式匹配来获取所需的部分:

$logFile = 'c:\temp\file.txt'
$outFile = 'c:\temp\file2.txt'

# read the content of the logfile as a single string
$content = Get-Content -Path $logFile -Raw

$regex = [regex] '(case\d+\s+\(BEF\)\s+ACT\s+\(AFT\)\s+BLK)'
$match = $regex.Match($content)
($output = while ($match.Success) {
    $match.Value
    $match = $match.NextMatch()
}) | Set-Content -Path $outFile -Force
使用时,结果为:

case1 (BEF) ACT
      (AFT) BLK
case7 (BEF) ACT
      (AFT) BLK
正则表达式详细信息:

  • 完成您自己的基于
    选择字符串的解决方案尝试
    选择字符串
    用途广泛,但速度较慢
    ,尽管它适合处理太大而无法放入整个内存的文件,因为它逐行处理文件

    • 但是,PowerShell提供了一种更快的逐行处理替代方案:
      -请参阅下面的解决方案
  • ,它首先将整个文件读入内存,根据文件大小的不同,总体性能可能最好,但由于严重依赖直接使用.NET功能,它的复杂性会增加


注意:仅当您希望将输出直接发送到管道到cmdlet(如
设置内容
)时,才需要封闭的
$(…)
;在变量中捕获输出时不需要它:
$pair=switch…

  • -Regex
    将分支条件解释为

  • 分支的操作脚本块(
    {…}
    中的
    $\
    指的是手头的行

  • 总体做法是:

    • $firstLine
      一旦找到第一行,就会存储感兴趣的第一行,当找到第二行的模式并且设置了
      $firstLine
      时(非空),就会输出该对
    • default
      处理程序重置
      $firstLine
      ,以确保只考虑包含感兴趣字符串的两个连续行

Brilliant!我必须阅读更多关于正则表达式的内容。非常感谢!我已经开发了一些使用-Pattern和-Context的其他方法:$infle='c:\temp\blablablablafile.txt'选择字符串模式'(BEF)ACT'$infle-Context 0,1 | ForEach对象{$lineAfter=$\上下文.PostContext[0]if($lineAfter.Contains('(AFT)BLK')){$\.Line,$lineAfter}}我明白了,@D.Kh.-看起来您从使用
-SimpleMatch'(BEF)ACT'
的字符串文字匹配切换回了使用
-Pattern'\(BEF\)的基于正则表达式的匹配请注意,由于您的正则表达式实际上不使用任何正则表达式构造,字符串文字匹配在语法上更简单、更快。这是一个非常好的解决方案!从未想过使用
开关
(+1)谢谢,@Theo.Yes,
switch-File
非常方便,但鲜为人知,大概是因为其他语言中的类似结构没有内置的文件处理能力,所以人们不会期望/记住它。
$logFile = 'c:\temp\file.txt'
$outFile = 'c:\temp\file2.txt'

# read the content of the logfile as a single string
$content = Get-Content -Path $logFile -Raw

$regex = [regex] '(case\d+\s+\(BEF\)\s+ACT\s+\(AFT\)\s+BLK)'
$match = $regex.Match($content)
($output = while ($match.Success) {
    $match.Value
    $match = $match.NextMatch()
}) | Set-Content -Path $outFile -Force
case1 (BEF) ACT
      (AFT) BLK
case7 (BEF) ACT
      (AFT) BLK
(              Match the regular expression below and capture its match into backreference number 1
   case        Match the characters “case” literally
   \d          Match a single digit 0..9
      +        Between one and unlimited times, as many times as possible, giving back as needed (greedy)
   \s          Match a single character that is a “whitespace character” (spaces, tabs, line breaks, etc.)
      +        Between one and unlimited times, as many times as possible, giving back as needed (greedy)
   \(          Match the character “(” literally
   BEF         Match the characters “BEF” literally
   \)          Match the character “)” literally
   \s          Match a single character that is a “whitespace character” (spaces, tabs, line breaks, etc.)
      +        Between one and unlimited times, as many times as possible, giving back as needed (greedy)
   ACT         Match the characters “ACT” literally
   \s          Match a single character that is a “whitespace character” (spaces, tabs, line breaks, etc.)
      +        Between one and unlimited times, as many times as possible, giving back as needed (greedy)
   \(          Match the character “(” literally
   AFT         Match the characters “AFT” literally
   \)          Match the character “)” literally
   \s          Match a single character that is a “whitespace character” (spaces, tabs, line breaks, etc.)
      +        Between one and unlimited times, as many times as possible, giving back as needed (greedy)
   BLK         Match the characters “BLK” literally
)
$(
  $firstLine = ''
  switch -CaseSensitive -Regex -File t.txt {
    '\(BEF\) ACT' { $firstLine = $_; continue }
    '\(AFT\) BLK' { 
      # Pair found, output it.
      # If you don't want to look for further pairs, 
      # append `; break` inside the block.
      if ($firstLine) { $firstLine, $_ }
      # Look for further pairs.
      $firstLine = ''; continue
    }
    default { $firstLine = '' }
  } 
) # | Set-Content ...