Использование Select-String для проверки двух файлов .txt в PowerShell - PullRequest
1 голос
/ 25 мая 2020

Я новичок в написании сценариев PowerShell. До сих пор я использовал обычную партию для своей цели, поскольку это требование моей компании. Внутри этого пакета я использую вложенные циклы для сравнения двух файлов .txt, подробнее я хочу сделать следующее:

  • Файл 1 содержит много строк. Каждая строка находится в одной отдельной строке с предшествующим номером и точкой с запятой, например: 658;RMS
  • Файл 2 - это длинный текст.

Цель состоит в том, чтобы подсчитать количество появлений каждой строки из файла 1 в файле 2, например, RMS подсчитывается 300 раз.

Поскольку мой предыдущий код имеет некоторые огромные недостатки, касающиеся время выполнения (файл 1 имеет примерно 400 строк, а файл 2 500.000) Я читал, что Select-String из Powershell намного эффективнее. Однако, поскольку я читаю некоторые руководства, мне непонятно, как я могу действовать здесь, кроме того, мне нужно запустить код PowerShell внутри моего .bat. Моя самая большая проблема в том, что я не уверен, как и где разместить свои «переменные», поэтому два входных файла 1 и 2

До сих пор я тестировал метод Select-String следующим образом:

powershell -command "& {Select-String -Path *.txt -Pattern "RMS"}"

Мое предположение заключалось в использовании трубопроводов, поэтому что-то вроде этого:

powershell -command "& {<<path to file one, should read line by line>> | Select-String -Path File2.txt -Pattern "value of file 1"}"

Однако я не могу заставить это работать. Powershell ожидает что-то вроде psobject перед первым конвейером?

Ответы [ 3 ]

2 голосов
/ 25 мая 2020

Для оптимальной производительности я бы подошел к этой задаче так.

  • Прочтите файл с терминами как CSV (это это CSV с ; разделитель)
  • Прочитать другой файл в строку
  • Для каждого термина подсчитать, как часто его можно найти в целевой строке (используя .IndexOf())

Например,

$data = Import-Csv "file1.txt" -Delimiter ";" -Header ID,Term 
$target = Get-Content "file2.txt" -Raw
$counts = @{}

foreach ($term in $data.Term) {
    $index = -1
    $count = 0
    do {
        $index = $target.IndexOf($term, $index + 1)
        if ($index -gt -1) { $count++ } else { break; }
    } while ($true);
    $counts[$term] = $count
}

$counts 

Примечания

  • Import-Csv автоматически будет использовать первую строку во входном файле в качестве заголовка. Если у вашего файла уже есть заголовок, вы можете удалить параметр -Headers.
  • Get-Content по умолчанию будет считывать входной файл в массив строк. Но для этого подхода использование всего файла в виде одной большой строки - это то, что делает -Raw.
  • @{} создает пустую хеш-таблицу
  • $data.Term будет обращаться к одной столбец CSV
  • .IndexOf() чувствителен к регистру. По умолчанию PowerShell нечувствителен к регистру, но нативные. NET методы, подобные этому, не изменят своего поведения. Это может быть, а может и не быть тем, что вам нужно - используйте .ToLower() на $target и $term, если вам не нужен чехол.
2 голосов
/ 25 мая 2020

Select-String это полезно , но это не магия c :)

Имея в виду влияние на производительность, я бы подошел к этому так:

  • Для каждой строки в File2:
    • Проверка на наличие всех терминов в File1

Таким образом , вам нужно только прочитать и вычислить File2 один раз :

# prepare hashtable to keep track of count
$count = @{}

# read terms to search for from file1
$termsToFind = Get-Content .\file1 |ForEach-Object {
  $_ -split ';' |Select -Last 1
}

# loop over lines in file2, count the words we're searching for
Get-Content .\test\file2 |ForEach-Object {
  foreach($term in $termsToFind){
    # Using `Regex.Matches()` will help us find multiple occurrences of the same term
    $count[$term] += [regex]::Matches($_,"\b$([regex]::Escape($term))\b").Count
  }
}

Теперь $count будет хеш-таблицей, где ключ - это член из файла1, а значение - это количество каждого слова.

Вывод в том же формате, что и file1 с:

$count.GetEnumerator() |ForEach-Object { $_.Value,$_.Key -join ';' } |Set-Content output.txt
1 голос
/ 25 мая 2020

Если вы проверяете документы, вы не можете передать -pattern по конвейеру для строки выбора. Вы можете использовать круглые скобки, чтобы результат чего-либо стал аргументом шаблона:

powershell select-string -pattern (get-content file1) -path file2    

Используя тот факт, что шаблон - это позиция 0, а путь - позиция 1. -pattern также может быть массивом.

powershell select-string (get-content file1) file2  
...