Как передать результаты в выходной массив

Поиграв какое-то время с некоторым скриптом powershell, мне было интересно, есть ли версия этого без использования С#. Такое ощущение, что мне не хватает некоторой информации о том, как правильно передавать вещи.

$packages = Get-ChildItem "C:\Users\A\Downloads" -Filter "*.nupkg" |
    %{ $_.Name } 
    # Select-String -Pattern "(?<packageId>[^\d]+)\.(?<version>[\w\d\.-]+)(?=.nupkg)" |
    # %{ @($_.Matches[0].Groups["packageId"].Value, $_.Matches[0].Groups["version"].Value) } 

         
foreach ($package in $packages){
    
    $match = [System.Text.RegularExpressions.Regex]::Match($package, "(?<packageId>[^\d]+)\.(?<version>[\w\d\.-]+)(?=.nupkg)")
    Write-Host "$($match.Groups["packageId"].Value) - $($match.Groups["version"].Value)"  
}

Первоначально я пытался сделать это только с помощью powershell и думал, что с помощью @(1,2,3) вы можете создать массив.

В итоге я обошел проблему, выполнив регулярное выражение с С# вместо powershell, что работает, но мне любопытно, как это можно было бы сделать только с помощью powershell.

В то время как есть 4 пакета, только версия powershell произвела 8 строк. Таким образом, доступ к моим данным, таким как $packages[0][0], для получения идентификатора пакета никогда не работал, потому что 8 строк были строками, в то время как я ожидал, что будут возвращены 4 массива.

Что вы имеете в виду под "без использования С#"? В вашем вопросе нет ни одной строки С#...

Mathias R. Jessen 07.10.2022 16:43

@MathiasR.Jessen [System.Text.RegularExpressions.Regex]::Match — это функциональность C#, а не powershell

Dbl 07.10.2022 16:44

Это действительно не «функциональность С#», это общая функциональность :)

Mathias R. Jessen 07.10.2022 16:48
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
99
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Примечание по терминологии без использования С#: вы имеете в виду без прямого использования .NET API. Напротив, C# — это просто еще один язык на основе .NET, который может использовать такие API, как и сам PowerShell.

Примечание:

  • Следующий раздел отвечает на следующий вопрос: как избежать прямых вызовов API-интерфейсов .NET для моего кода сопоставления регулярных выражений в пользу использования собственных команд PowerShell (операторов, автоматических переменных)?

  • См. Нижний раздел для решения Select-String, которое было вашей истинной целью; tl;dr:

    # Note the `, `, which ensures that the array is output *as a single object*
    %{ , @($_.Matches[0].Groups["packageId"].Value, $_.Matches[0].Groups["version"].Value) }
    

Собственный (почти) PowerShell-эквивалент вашего кода (обратите внимание, что предполагается, что $package содержит содержимое входного файла):

# Caveat: -match is case-INSENSITIVE; use -cmatch for case-sensitive matching.
if ($package -match '(?<packageId>[^\d]+)\.(?<version>[\w\d\.-]+)(?=.nupkg)') {
  "$($Matches['packageId']) - $($Matches['Version'])"  
}
  • -match, оператор сопоставления регулярных выражений , является эквивалентом [System.Text.RegularExpressions.Regex]::Match() (который можно сократить до [regex]::Match()) в том смысле, что он ищет только (не более ) одно совпадение.

    • Предостережение относительно учета регистра: -match (и его редко используемый псевдоним -imatch) по умолчанию нечувствительны к регистру, как и все операторы PowerShell; для соответствия с учетом регистра используйте вариант с префиксом c, -cmatch.

    • Напротив, API-интерфейсы .NET по умолчанию чувствительны к регистру; вам нужно будет передать флаг [System.Text.RegularExpressions.RegexOptions]::IgnoreCase в [regex]::Match() для совпадения без учета регистра (вы можете использовать 'IgnoreCase', который PowerShell автоматически конвертирует для вас).

    • Начиная с PowerShell 7.2.x, нет оператора, который был бы эквивалентен соответствующему API return-ALL-matches .NET, [regex]::Matches() . См. GitHub issue #7867, чтобы получить зеленый свет, но еще не реализованное предложение по внедрению одного из них под названием -matchall.

  • Однако вместо того, чтобы напрямую возвращать объект, описывающий, что было (или не было) сопоставлено, -match возвращает логическое значение, то есть $true или $false, чтобы указать, удалось ли сопоставление.

  • Информация о совпадении становится доступной только в том случае, если -match возвращает $true, а именно через автоматическую переменную $Matches , которая представляет собой хеш-таблицу, отражающую совпадающие части входной строки: запись 0 всегда является полным совпадением, с необязательным дополнительные записи, отражающие то, что захватили какие-либо группы захвата ((...)), либо по индексу, если они анонимные (начиная с 1), либо, как в вашем случае, для именованных групп захвата ((?<name>...)) по имени.

    • Примечание по синтаксису. Учитывая, что PowerShell позволяет использовать точечную нотацию (синтаксис доступа к свойствам) даже с хеш-таблицами, приведенная выше команда могла бы использовать, например, $Matches.packageId вместо $Matches['packageId'], что также работает с числовыми (на основе индекса) записями, например, $Matches.0 вместо $Matches[0]

    • Предостережение: если в качестве операнда LHS используется массив (перечисляемый), поведение -match меняется:

      • $Matches не заполняется.
      • выполняется фильтрация; то есть вместо возврата логического значения, указывающего, удалось ли сопоставление, возвращается подмассив совпадающих входных строк.
    • Обратите внимание, что хеш-таблица $Matches предоставляет только совпадающие строки, а не метаданные, такие как индекс и длина, как в возвращаемом объекте [regex]::Match(), который имеет тип [System.Text.RegularExpressions.Match].


Решение Select-String:

$packages | 
  Select-String '(?<packageId>[^\d]+)\.(?<version>[\w\d\.-]+)(?=.nupkg)' |
  ForEach-Object {
    "$($_.Matches[0].Groups['packageId'].Value) - $($_.Matches[0].Groups['version'].Value)"
  }
  • Select-String выводит экземпляры Microsoft.PowerShell.Commands.MatchInfo, чья .Matches коллекция содержит один или несколько экземпляров [System.Text.RegularExpressions.Match], т. е. экземпляров того же типа, что и возвращаемый [regex]::Match()

    • Если -AllMatches также не передается, .Matches имеет только одну запись, поэтому используется [0] для нацеливания на эту запись выше.

Как видите, работа с выходными объектами Select-Object требует, чтобы вы в конечном итоге работали с тем же типом .NET, что и при непосредственном вызове [regex]::Match().
Однако вызовы методов не требуются, а обнаружение свойств выходных объектов в PowerShell упрощается с помощью командлета Get-Member.


Если вы хотите зафиксировать совпадения в зубчатом массиве:

$capturedStrings = @(
  $packages | 
    Select-String '(?<packageId>[^\d]+)\.(?<version>[\w\d\.-]+)(?=.nupkg)' |
    ForEach-Object {
      # Output an array of all capture-group matches, 
      # *as a single object* (note the `, `) 
      , $_.Matches[0].Groups.Where({ $_.Name -ne '0' }).Value 
    }
)

Это возвращает массив массивов, каждый элемент которого является массивом совпадений группы захвата для данного пакета, так что $capturedStrings[0][0] возвращает значение packageId, например, для первого пакета.

Примечание:

  • $_.Matches[0].Groups.Where({ $_.Name -ne '0' }).Value программно перечисляет все совпадения группы захвата и возвращает значения их .Value свойств в виде массива, используя перечисление доступа к членам; обратите внимание, что имя '0' должно быть исключено, так как оно представляет собой полное совпадение.

    • С группами захвата в вашем конкретном регулярном выражении приведенное выше эквивалентно следующему, как показано в закомментированной строке вашего вопроса:

      @($_.Matches[0].Groups['packageId'].Value, $_.Matches[0].Groups['version'].Value)
      
  • , ..., унарная форма оператора построения массива , используется в качестве ярлыка для вывода массива (обозначенного здесь ...) в целом, как единый объект. По умолчанию будет происходить перечисление, и элементы будут испускаться один за другим. , ... на самом деле является ярлыком для концептуально более ясного Write-Output -NoEnumerate ... - см. этот ответ для объяснения техники.

  • Кроме того, @(...), оператор подвыражения массива необходим для того, чтобы гарантировать, что будет возвращен зубчатый массив (вложенный массив) даже в том случае, если во всех $packages будет возвращен только один массив.

@Dbl, теперь я вижу, что пропустил закомментированные строки во фрагменте кода в вашем вопросе, которые действительно показывают, что унарная , была единственной отсутствующей частью. Я просто замалчивал их, полагая, что они не важны из-за того, что они закомментированы. В любом случае, я рад, что мы нашли решение.

mklement0 07.10.2022 18:29

Другие вопросы по теме