Что представляет собой "линию" для метода 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) окончания строки.)
- (The
Короче:
"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
, но я хочу просто добавить несколько пунктов, чтобы поддержать его ответ.
Я сделал некоторые обратные инженерные работы против этого
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 как целую строку, он не выполняет никакого разделения.
Я не нахожу фактический исходный код этого командлета на github, возможно, эта служебная часть еще не является открытым исходным кодом. Но я нахожу модульный тест этого
Select-String
,$testinputone = "hello","Hello","goodbye" $testinputtwo = "hello","Hello"
Тестовые строки, которые они используют для модульного тестирования, на самом деле являются списками строк. Это означает, что они даже не думали о вашем сценарии использования и, возможно, он просто предназначен для приема ввода коллекции строк.
Однако если мы посмотрим на официальный документ 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"