Powershell ищет строку между двумя строками

#string #powershell #txt

#строка #powershell #txt

Вопрос:

Как я могу проверить, есть ли значение «данные» между моими двумя другими «значениями», зная, что количество строк, разделяющих их, не является регулярным и что последние «данные» не имеют «значения» после него?

 values
xxx
xxx
data
xxx
values
xxx
xxx
values
xxx
data
values
xxx
data
xxx
 

Все, что у меня есть, это:

     if (xxx) {
        xxx | Add-Content -Path $DATA
    } else {
        Add-Content -Path $DATA -Value 'N/A'
    }
 

И в итоге я хотел бы получить результат :

 data
N/A
data
data
 

Ответ №1:

Вам нужно будет читать строки одну за другой и отслеживать 1) когда вы видите values строку и 2) когда вы видите data строку.

Я предпочитаю switch операторы для такого нисходящего синтаксического анализа:

 # In real life you would probably read from a file on disk, ie.
# $data = Get-Content somefile.txt
$data = -split 'values xxx xxx data xxx values xxx xxx values xxx data values xxx data xxx'

# Use this to keep track of whether the first "values" line has been observed 
$inValues = $false

# Use this to keep track of whether a "data" line has been observed since last "values" line
$hasData = $false

switch($data)
{
  'values' {
    # don't output anything the first time we see `values`
    if($inValues){
      if($hasData){
        'data'
      } else {
        'N/A'
      }
    }
    
    $inValues = $true
    # reset our 'data' monitor
    $hasData = $false
  }

  'data' {
    # we got one!
    $hasData = $true
  }
}

if($inValues){
  # remember to output for the last `values` block
  if($hasData){
    'data'
  } else {
    'N/A'
  }
}
 

Ответ №2:

Вы можете использовать Select-String подход:

 # $s contains a single string of your data
($s | Select-String -Pattern '(?s)(?<=values).*?(?=values|$)' -AllMatches).Matches.Value |
    Foreach-Object {
        ('N/A',$matches.0)[$_ -match 'data']
    }
 

Объяснение:

-Pattern без -SimpleMatch использования регулярных выражений. (?s) является однострочным модификатором, который . соответствует символам новой строки. (?<=values) является положительным поиском для строки. values (?=values|$) является положительным ориентиром для строки values или ( | ) конца строки ( $ ) . Использование lookaheads и lookbehinds values предотвращает сопоставление, чтобы каждый из них можно было использовать в следующем наборе совпадений. В противном случае, после values того как он будет сопоставлен, он не сможет быть использован снова в другом условии соответствия. .*? лениво сопоставляет все символы.

Внутри Foreach-Object проверяется текущий объект соответствия $_ data . Если data найдено, автоматическая переменная $matches обновляется с ее значением. Поскольку $matches это хэш-таблица, вам необходимо получить доступ к группе захвата 0 ( 0 это имя ключа) для получения значения.

Здесь обязательно должна $s быть одна строка. Если вы читаете его из текстового файла, используйте $s = Get-Content file.txt -Raw .


Вы могли бы сделать это немного более динамичным, используя переменные:

 $Start = 'values'
$End = 'values'
$data = 'data'
($s | Select-String -Pattern "(?s)(?<=$Start).*?(?=$End|$)" -AllMatches).Matches.Value |
    Foreach-Object {
        ('N/A',$matches.0)[$_ -match 'data']
    }
 

Комментарии:

1. Спасибо, что вы мне очень помогли, но у меня все еще есть две проблемы. Первый заключается в том, что я выполняю другое совпадение раньше и, очевидно, последнее совпадение возобновляется, и я нахожу его в своем списке, поэтому я не знаю, есть ли способ его сбросить. Второй заключается в том, что когда я пытаюсь выполнить поиск нескольких «данных» таким образом: Foreach-Object { (‘N / A’,$matches.0)[$_ -match ‘Data1’] (‘N / A’,$matches.0)[$_ -match ‘Data2’] (‘N / A’,$matches.0)[$_ -сопоставление ‘Data3’] } «N / A» всегда ставят себя на первое место, и поэтому данные выводятся не в правильном порядке.

2. Для второй проблемы просто используйте ('N/A',$matches.0)[$_ -match 'Data1|Data2|Data3']