Как сравнить две последовательные строки в файле - PullRequest
1 голос
/ 16 апреля 2019

У меня есть большой файл, состоящий из случаев «до» и «после» для каждого элемента следующим образом:

case1 (BEF) ACT
      (AFT) BLK
case2 (BEF) ACT
      (AFT) ACT
case3 (BEF) ACT
      (AFT) CLC
...

Мне нужно выбрать все строки, которые имеют (BEF) ACT на первой"string и (AFT) BLK на" second "и поместите результат в файл.

Идея состоит в том, чтобы создать предложение типа

IF (stringX.LineNumber consists of "(BEF) ACT" AND stringX+1.LineNumber consists of (AFT) BLK)
{OutFile $stringX+$stringX+1}

Извините за синтаксис, ятолько начинаю работать с PS:)

$logfile = 'c:\temp\file.txt'
$matchphrase = '\(BEF\) ACT'
$linenum=Get-Content $logfile | Select-String $matchphrase | ForEach-Object {$_.LineNumber+1}
$linenum 
#I've worked out how to get a line number after the line with first required phrase

Создайте новый файл с результатом: строка с «(BEF) ACT», следующая со строкой с «(AFT) BLK»

Ответы [ 3 ]

1 голос
/ 16 апреля 2019

Другим способом сделать это является чтение $ logFile в виде одной строки и использование соответствия RegEx для получения нужных вам частей:

$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
)
1 голос
/ 16 апреля 2019
  • Мой другой ответ завершает вашу собственную попытку решения на основе Select-String.Select-String является универсальным, но медленным , хотя он подходит для обработки файлов, слишком больших для размещения в памяти в целом, поскольку он обрабатывает файлы построчно .

    • Однако PowerShell предлагает a намного быстрее альтернативный вариант построчной обработки:
      switch -File
      - см. Решение ниже.
  • Полезный ответ Тео , который сначала считывает весь файл в память, вероятно, будет лучше всего в целом, в зависимости от размера файла, но он приходит встоимость повышенной сложности из-за большой зависимости от прямого использования функциональности .NET.


$(
  $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 ...

Примечание. Вложение $(...) необходимо только в том случае, есливы хотите отправить вывод напрямую в конвейер командлету, например Set-Content;он не нужен для захвата вывода в переменную : $pair = switch ...

  • -Regex интерпретирует условные переходы как регулярные выражения .

  • $_ внутри блока сценария действий ветви ({ ... } относится к текущей строке.

  • Общий подходis:

    • $firstLine хранит 1-ю интересующую линию, как только найден, и когда шаблон 2-й линии найден и установлен $firstLine (не пуст), выдается пара.
    • Обработчик default сбрасывает $firstLine, чтобы обеспечить рассмотрение только двух последовательных строк, содержащих интересующие строки.
1 голос
/ 16 апреля 2019
Select-String -SimpleMatch -CaseSensitive '(BEF) ACT' c:\temp\file.txt -Context 0,1 |
  ForEach-Object {
    $lineAfter = $_.Context.PostContext[0]
    if ($lineAfter.Contains('(AFT) BLK')) {
      $_.Line, $lineAfter  # output
    }
  } # | Set-Content ...
  • -SimpleMatch выполняет сопоставление подстроки строки-литерала, что означает, что вы можете передать строку поиска как есть, без необходимости экранировать ее.

    • Однако, если вам необходимо дополнительно ограничить поиск, например, обеспечить его выполнение только в конце строки ($), вам действительно понадобится регулярное выражение с (подразумеваемым) -Pattern параметром: '\(BEF\) ACT$'

    • Также обратите внимание, что PowerShell обычно регистр- нечувствителен по умолчанию, поэтому используется переключатель -CaseSensitive.

  • Обратите внимание, как Select-String может принимать пути к файлам напрямую - нет необходимости в предыдущем вызове Get-Content.

  • -Context 0,1 захватывает 0 строки до и 1 строки после каждого матча и включает их в [Microsoft.PowerShell.Commands.MatchInfo] случаи, когда Select-String выводит.

  • Внутри блока сценариев ForEach-Object, $_.Context.PostContext[0] извлекает строку после совпадения и .Contains() выполняет поиск в ней буквенной подстроки.

    • Обратите внимание, что .Contains() - это метод типа .NET System.String, и такие методы, в отличие от PowerShell, по умолчанию чувствительны к регистру 1061 *, но вы можете использовать необязательный параметр, чтобы изменить его. ,
  • Если подстрока найдена в следующей строке, выводится как строка под рукой, так и последующая.

  • Вышеприведенный поиск ищет всех совпадающих пар во входном файле; если вы хотите найти только пару first , добавьте | Select-Object -First 2 к вызову Select-String.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...