Как изменить foreach на for в PowerShell? - PullRequest
0 голосов
/ 08 марта 2019

Я хочу напечатать слово «существует» в текстовом файле и напечатать «соответствует» и «не соответствует».Мой 1-й текстовый файл: xxaavv6J, мой 2-й файл 6J6SCa.yB.

Если он совпадает, он возвращается так:

Match found: 
Match found: 
Match found: 
Match found: 
Match found: 
Match found: 6J
Match found: 
Match found: 
Match found: 

Я ожидаю только печать match и not match.

$X = Get-Content "C:\Users\2.txt"
$Data = Get-Content "C:\Users\d.txt"
$Split = $Data -split '(..)'

$Y = $X.Substring(0, 6)

$Z = $Y -split '(..)'

foreach ($i in $Z) {
    foreach ($j in $Split) {
        if ($i -like $j) { 
            Write-Host ("Match found: {0}" -f $i, $j)
        }
    }
}

Ответы [ 3 ]

2 голосов
/ 08 марта 2019

Операция -split '(..)' не дает ожидаемого результата. Если вы посмотрите на вывод следующей команды, то увидите, что вы получаете много пустых результатов:

PS C:\> <b>'xxaavv6J' -split '(..)' | % { "-$_-" }</b>
--
-xx-
--
-aa-
--
-vv-
--
-6J-
--

Эти пустые значения являются дополнительными совпадениями, которые вы получаете от $i -like $j.

Я не совсем уверен, почему -split '(..)' вообще дает вам непустые значения, потому что я ожидал, что он выдаст 5 пустых строк для входной строки "xxaavv6J". По-видимому, это связано с круглыми скобками для группировки, поскольку -split '..' (без скобок для группировки) на самом деле ведет себя так, как ожидалось. Похоже, с группой захвата полученные совпадения возвращаются поверх результатов операции разделения.

В любом случае, чтобы получить поведение, которое вы хотите заменить

... -split '(..)'

с

... |
    Select-String '..' -AllMatches |
    Select-Object -Expand Matches |
    Select-Object -Expand Value

Вы также можете заменить вложенный цикл чем-то вроде этого:

foreach ($i in $Z) {
    if (if $Split -contains $i) {
        Write-Host "Match found: ${i}"
    }
}
0 голосов
/ 08 марта 2019

В дополнение к превосходному ответу Ansgar Wiechers : если вы используете (выше) Windows PowerShell 4.0 , тогда вы можете применить метод .Where(), описанный в Кирк Мунро исчерпывающая статья ForEach и где магические методы :

С выпуском Windows PowerShell 4.0 появились два новых «волшебных» метода были введены для типов коллекций, которые предоставляют новый синтаксис для доступ к возможностям ForEach и Where в Windows PowerShell. Эти методы точно названы ForEach и Where. Я звоню эти методы «магические», потому что они довольно волшебны в том, как они работают в PowerShell. Они не отображаются в выводе Get-Member, даже если вы применить -Force и запрос -MemberType All. Если вы закатываете рукава и копайся с отражением, ты можешь их найти; однако это требует широкого поиска, потому что они являются частными методами расширения реализовано в закрытом классе . Тем не менее, даже если они не обнаруживается, не заглядывая под одеяло, они есть, когда вы они нужны, они быстрее, чем их старшие коллеги, и они включать функции, которые не были доступны в их старых коллег, отсюда и «волшебное» чувство, которое они покидают, когда вы используйте их в PowerShell. К сожалению, эти методы остаются без документов даже сегодня, почти год, так как они были публично так много людей не осознают силу, которая доступна в эти методы.
...

Метод Where

Where - это метод, который позволяет фильтровать коллекцию объектов. Это очень похоже на командлет Where-Object, но Where метод также как Select-Object и Group-Object, включает несколько дополнительных функций, которые Where-Object командлет изначально не поддерживает сам по себе. Этот метод обеспечивает быстрее производительность, чем Where-Object в простой, элегантной команде. подобно метод ForEach, любые объекты, которые выводятся этим методом возвращается в общую коллекцию типа System.Collections.ObjectModel.Collection1[psobject].

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

Where(scriptblock expression[, WhereOperatorSelectionMode mode[, int numberToReturn]])

Как указано в квадратных скобках, блок сценария expression требуется и перечисление mode и целое число numberToReturn аргумент не является обязательным, поэтому вы можете вызвать этот метод, используя 1, 2 или 3 аргументы. Если вы хотите использовать определенный аргумент, вы должны предоставить все аргументы слева от этого аргумента (т.е. если вы хотите укажите значение numberToReturn, вы должны указать mode и expression)

Применительно к вашему случаю (используя самый простой вариант Where(scriptblock expression) метода .Where()):

$X    = '6J6SCa.yB'  # Get-Content "C:\Users\2.txt"
$Data = 'xxaavv6J'   # Get-Content "C:\Users\d.txt"

$Split = ($Data -split '(..)').Where({$_ -ne ''})

$Y = $X.Substring(0, 6)
$Z = ($Y -split '(..)').Where{$_ -ne ''}    # without parentheses

Например, пример Ансгара изменяется следующим образом:

PS > ('xxaavv6J' -split '(..)').Where{$_ -ne ''} | % { "-$_-" }
-xx-
-aa-
-vv-
-6J-
0 голосов
/ 08 марта 2019

Немного другой подход с использованием регулярного выражения '.Match ()' также должен сделать это.
Я добавил много поясняющих комментариев для вас:

$Test = Get-Content "C:\Users\2.txt" -Raw         # Read as single string. Contains "xxaavv6J"
$Data = (Get-Content "C:\Users\d.txt") -join ''   # Read as array and join the lines with an empty string. 
                                                  # This will remove Newlines. Contains "6J6SCa.yB"

# Split the data and make sure every substring has two characters
# In each substring, the regex special characters need to be Escaped.
# When this is done, we join the substrings together using the pipe symbol.
$Data = ($Data -split '(.{2})' |                                   # split on every two characters
        Where-Object { $_.Length -eq 2 } |                         # don't care about any left over character
        ForEach-Object { [Regex]::Escape($_) } ) -join '|'         # join with the '|' which is an OR in regular expression

# $Data is now a string to use with regular expression: "6J|6S|Ca|\.y"

# Using '.Match()' works Case-Sensitive. To have it compare Case-Insensitive, we do this:
$Data = '(?i)' + $Data

# See if we can find one or more matches
$regex = [regex]$Data
$match = $regex.Match($Test)
# If we have found at least one match:
if ($match.Groups.Count) {
    while ($match.Success) {
        # matched text: $match.Value
        # match start:  $match.Index
        # match length: $match.Length
        Write-Host ("Match found: {0}" -f $match.Value)
        $match = $match.NextMatch()
    }
}
else {
    Write-Host "Not Found"
}

Результат:

Match found: 6J

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...