Метод поиска не работает в сценарии Powershell

Я пытаюсь использовать .net API для поиска в большом файле данных. По какой-то причине я не могу заставить его работать. Вот мой код:

function check_logs{
  $pos = 8192
  $count = 1
  $path = 'C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\Log\ERRORLOG.2'
  $br = 0
  $reader = [System.IO.File]::OpenText($path)
  $reader.DiscardBufferedData()
  $reader.BaseStream.Seek(0, [System.IO.SeekOrigin]::Begin)
    for(;;){
    $line = $reader.ReadLine()
    if($line -ne $null){$br = $br + [System.Text.Encoding]::UTF8.GetByteCount($line)}
    if($line -eq $null -and $count -eq 0){break}
    if($line -eq $null){$count = 0}
    elseif($line.Contains('  Error:')){
        Write-Host "$line  $br"
    }
}

}

Если я использую 0 в качестве параметра для метода seek, он ищет с самого начала, как и ожидалось, но также выводит 0 на консоль перед записью прочитанных строк. Пример:

 0
 2011-08-31 09:26:36.31 Logon       Error: 17187, Severity: 16, State: 1.  4101
 2011-08-31 09:26:36.32 Logon       Error: 17187, Severity: 16, State: 1.  4489
 2011-08-31 09:26:38.25 Logon       Error: 17187, Severity: 16, State: 1.  4929
 2011-08-31 09:26:38.25 Logon       Error: 17187, Severity: 16, State: 1.  5304
 2011-08-31 09:26:43.75 Logon       Error: 17187, Severity: 16, State: 1.  6120

Если я попытаюсь выполнить поиск, используя 4096 вместо 0, он выдаст только:

4096

Я бы подумал, что он напишет те же строки, что и первый, кроме первых двух.

Может ли кто-нибудь увидеть проблему? У меня был другой вопрос, который привел меня к этому. Дополнительную информацию см. в этом.

РЕДАКТИРОВАТЬ: Все еще пытаюсь понять это. Кто-нибудь знает, где еще я мог бы попытаться найти информацию об этой проблеме? Можно ли отправить вопросы сценаристу Microsoft?

С наилучшими пожеланиями

Гисли


person Gisli    schedule 01.09.2011    source источник


Ответы (3)


Метод Seek возвращает новую позицию в потоке, поэтому вы печатаете число.

Что касается того, почему вы не получаете вывод:

  1. Убедитесь, что размер файла превышает 4 КБ.
  2. Попробуйте распечатать все строки, а не только строки со словом «Ошибка». Это может дать вам подсказку
  3. StreamReader — это буферизованная оболочка вокруг основного потока, поэтому Seek и Position могут работать не совсем так, как вы ожидаете. Рассмотрим http://geekninja.blogspot.com/2007/07/streamreader-annoying-design-decisions.html. Попробуйте добавить вызов $reader.DiscardBufferedData() перед поиском.
person RB.    schedule 01.09.2011
comment
@RB - Спасибо за ответ. Неважно, что я использую вместо 0, если я поставлю 1, вывод равен 1. Также, если вы посмотрите на вывод, когда я использую 0, последнее число должно быть количеством прочитанных байтов. Или, по крайней мере, я так думаю... - person Gisli; 01.09.2011
comment
Точно. Поиск возвращает смещение, которое вы искали. Поэтому, если вы ищете 1234, он вернет 1234. См. Возвращаемое значение в msdn.microsoft.com/en-us/library/system.io.stream.seek.aspx - person RB.; 01.09.2011
comment
@RB - Хорошо, это многое объясняет. Я думал, что это переместит меня на определенную позицию в потоке. Как мне тогда начать чтение с этой позиции? - person Gisli; 01.09.2011
comment
@Гисли. Не совсем уверен, но я думаю, что вызов DiscardBufferedData после поиска заставит StreamReader повторно заполнить буфер из позиции поиска, что даст вам то, что вы хотите. - person RB.; 01.09.2011
comment
А, в этой статье предлагается называть его перед поиском, что на самом деле имеет больше смысла! Попробуйте оба и дайте мне знать, что работает :) msdn. microsoft.com/en-us/library/ - person RB.; 01.09.2011
comment
@RB - Спасибо, но это не работает. Я пытался использовать функцию DiscardBufferedData() как до, так и после поиска, но это не имеет значения. - person Gisli; 01.09.2011

Итак, я наконец нашел ответ. По какой-то неизвестной мне причине я должен использовать двоичный ридер. Ниже приведена моя полная функция:

 function check_logs{
 Write-Host "New test `n`n"
 $pos = 19192
 $path = 'C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\Log\ERRORLOG.2'
 $br = 0
 $b = new-object System.IO.BinaryReader([System.IO.File]::Open($path,[System.IO.FileMode]::Open));
 $required = $b.BaseStream.Length - $pos
 $b.BaseStream.Seek($pos, [System.IO.SeekOrigin]::Begin)
 $bytes = $b.ReadBytes($required)
 $log = [System.Text.Encoding]::Unicode.GetString($bytes)
 $split = $log.Split("`n")
 foreach($s in $split)
 {
     if($s.contains("  Error:"))
     {
         Write-Host $s  "`n"
     }
 }
 $b.close
 }

Спасибо за помощь

Гисли

person Gisli    schedule 12.09.2011
comment
Проблема возникает из-за того, как интерпретируется текст. Похоже, вам повезло, потому что базовые данные файла представляют собой однобайтовую кодировку. Ваше решение не будет работать в Unicode и иногда будет работать только с файлами UTF-8, в которых есть неанглийские символы. - person Granger; 09.04.2020

У меня была аналогичная проблема. Искомая позиция печаталась на консоли. Мне просто нужно было присвоить возвращаемое значение некоторой переменной, и это решило проблему.

Итак, вместо:

$reader.BaseStream.Seek(0, [System.IO.SeekOrigin]::Begin)

Мне пришлось написать что-то вроде:

$pos = $reader.BaseStream.Seek(0, [System.IO.SeekOrigin]::Begin)

С уважением, Теясви В.

person Thejasvi V    schedule 30.01.2014