Что представляет собой "линию" для метода Select-String в Powershell?

Я ожидаю, что Select-String рассматривать \r\n (возврат каретки + перевод строки) конец строки в Powershell.

Однако, как видно ниже, abc соответствует целому входу:

PS C:\Tools\hashcat> "abc`r`ndef" | Select-String -Pattern "abc"

abc
def

Если я разобью строку на две части, то Select-String ведет себя так, как я ожидал:

PS C:\Tools\hashcat> "abc", "def" | Select-String -Pattern "abc"

abc

Как я могу дать Select-String строка, строки которой заканчиваются на \r\n, а затем сделать этот командлет возвращает только те строки, которые содержат совпадение?

3 ответа

Решение
  • Select-String работает с каждым (строковым по запросу [1]) входным объектом.

  • Многострочная строка, такая как "abc`r`ndef" это один объект ввода.

    • В отличие от "abc", "def" массив строк с двумя элементами, передаваемый как два входных объекта
  • Чтобы убедиться, что строки многострочной строки передаются по отдельности, разбейте строку на массив строк с помощью PowerShell. -split оператор: "abc`r`ndef" -split "`r?`n"

    • (The ? делает `r необязательно, чтобы также правильно иметь дело с `n только (LF-only, Unix-style) окончания строки.)

Короче:

"abc`r`ndef" -split "`r?`n" | Select-String -Pattern "abc"

Это эквивалентно использованию строкового литерала PowerShell с escape-последовательностями регулярного выражения (регулярное выражение) -split это регулярное выражение):

"abc`r`ndef" -split '\r?\n' | Select-String -Pattern "abc"

Несколько прискорбно, что Select-String В документации говорится о работе со строками текста, учитывая, что действительными единицами операций являются входные объекты, которые, как мы видели, сами могут содержать несколько строк.
Предположительно, это происходит из типичного случая использования ввода объектов через Get-Content командлет, который выводит строки текстового файла одну за другой.

Обратите внимание, что Select-String не возвращает соответствующие строки напрямую, а переносит их в [Microsoft.PowerShell.Commands.MatchInfo] объекты, содержащие полезные метаданные о совпадении. Даже там присутствует метафора линии, так как она .Line свойство, которое содержит соответствующую строку.


[1] Дополнительное чтение: как Select-String строковые объекты ввода

Если входной объект уже не является строкой, он конвертируется в один, хотя, возможно, не так, как вы ожидаете:

Грубо говоря, .ToString() метод вызывается для каждого нестрокового входного объекта [2], который для нестроковых не совпадает с представлением, которое вы получаете с форматированием вывода PowerShell по умолчанию (последнее - то, что вы видите, когда вы печатаете объект на консоль или используете Out-File, например); напротив, это то же представление, которое вы получаете при интерполяции строк в строке в двойных кавычках (когда вы вставляете ссылку на переменную или команду в "..." например, "$HOME" или же "$(Get-Date)").

Часто, .ToString() просто выдает имя типа объекта, без какой-либо специфичной для экземпляра информации; например, $PSVersionTable переводит в System.Management.Automation.PSVersionHashTable,

# Matches NOTHING, because Select-String sees
# 'System.Management.Automation.PSVersionHashTable' as its input.
$PSVersionTable | Select-String PSVersion 

Если вы хотите искать формат вывода по умолчанию построчно, используйте следующую идиому:

... | Out-String -Stream | Select-String ...

Однако обратите внимание, что для нестрокового ввода более надежным и предпочтительным для последующей обработки является фильтрация ввода путем запроса свойств с Where-Object состояние.


[2] Точнее, .psobject.ToString() называется, как есть, или - если объект ToString метод поддерживает IFormatProvider аргумент - как .psobject.ToString([cultureinfo]::InvariantCulture) чтобы получить представление, инвариантное к культуре - см. мой ответ для справки.

В основном г-н Гюнтер Шмитц объяснил правильное использование Select-String, но я хочу просто добавить несколько пунктов, чтобы поддержать его ответ.

  1. Я сделал некоторые обратные инженерные работы против этого Select-String Командлет. Это в Microsoft.PowerShell.Utility.dll. Ниже приведены некоторые соответствующие фрагменты кода. Обратите внимание, что для справки - это коды от реинжиниринга, а не исходный код.

    string text = inputObject.BaseObject as string;
    ...
    matchInfo = (inputObject.BaseObject as MatchInfo);
    object operand = ((object)matchInfo) ?? ((object)inputObject);
    flag2 = doMatch(operand, out matchInfo2, out text);
    

    Мы можем выяснить, что он просто обрабатывает inputObject как целую строку, он не выполняет никакого разделения.

  2. Я не нахожу фактический исходный код этого командлета на github, возможно, эта служебная часть еще не является открытым исходным кодом. Но я нахожу модульный тест этого Select-String,

    $testinputone = "hello","Hello","goodbye"
    $testinputtwo = "hello","Hello"
    

    Тестовые строки, которые они используют для модульного тестирования, на самом деле являются списками строк. Это означает, что они даже не думали о вашем сценарии использования и, возможно, он просто предназначен для приема ввода коллекции строк.

  3. Однако если мы посмотрим на официальный документ Microsoft относительно Select-String мы видим, что он много говорит о строке, но не может распознать строку в строке. Мое личное предположение состоит в том, что концепция строки имеет смысл только тогда, когда командлет принимает файл в качестве входных данных, в случае, если файл похож на список строк, каждый элемент в списке представляет одну строку.

Надеюсь, что это может прояснить ситуацию.

"abc`r`ndef"

одна строка, которая, если вы эхо (Write-Output) в консоли приведет к:

PS C:\Users\gpunktschmitz> echo "abc`r`ndef"
abc
def

Select-String будет выводить каждую строку, где "abc" является ее частью. Поскольку "abc" является частью строки, эта строка будет выбрана.

"abc", "def"

это список из двух строк. С использованием Select-String здесь сначала проверяется "abc", а затем "def", если шаблон соответствует "abc". Поскольку только первый соответствует только он будет выбран.

Используйте следующую команду, чтобы разбить строку на список и выбрать только элементы, содержащие "abc"

"abc`r`ndef".Split("`r`n") | Select-String -Pattern "abc"
Другие вопросы по тегам