Необходимо создать цикл foreach для файла CSV, используя 2 переменные, в Powershell

Я новичок в программировании, и большая часть моего опыта связана с Python. Я работаю над простым сценарием PowerShell, который запускает Test-NetConnection по IP-адресу и портам в CSV-файле.

ИП Порт 1.1.1.1 22 1.1.1.2 25 1.1.1.3 80 1.1.1.4

Каждый IP необходимо протестировать с каждым портом.

На данный момент у меня есть следующее: я не уверен, нужно ли мне создавать новый список для каждой комбинации IP/порта, а затем запускать для них foreach:

$csv_path = Read-Host -Prompt "Enter file path"

$csv = Import-Csv -Path "$csv_path"

foreach($item in $csv){
    "{0} = {1}" -f $item.IP,$item.Port
    Test-NetConnection -ComputerName "$item.IP" -Port "$item.Port" -InformationLevel Detailed
}

🤔 А знаете ли вы, что...
С помощью PowerShell можно аудитить и мониторить действия пользователей в системе.


57
4

Ответы:

Каждый IP необходимо протестировать с каждым портом.

Итак, вы проверяете порты 22, 25 и 80 для всех четырех IP-адресов? Всего 12 проверок на основе выборочных данных?

Данные CSV должны работать не так, но ладно, мы все равно можем это сделать.

$csv_path = Read-Host -Prompt "Enter file path"

$csv = Import-Csv -Path $csv_path

foreach($itemIP in $csv | ?{$_.IP.Length -gt 0}) {
    foreach($itemPort in $csv | ?{$_.Port.Length -gt 0}) {
        "$($itemIP.IP) = $($itemPort.Port)"
        Test-NetConnection -ComputerName "$($itemIP.IP)" -Port "$($itemPort.Port)" -InformationLevel Detailed
    }
}

Решено

В PowerShell есть удобная функция Перечисление доступа к участникам, которую вы можете использовать здесь.

Короче говоря, если вы получаете доступ к свойству или методу объекта массива (например, вашей переменной $csv), он вызовет это свойство или метод для всех элементов массива и вернет новый массив со всеми результатами в нем.

Например, с этими тестовыми данными:

PS> $csv = @"
IP,      Port
1.1.1.1, 22
1.1.1.2, 25
1.1.1.3, 80
1.1.1.4,
"@ | ConvertFrom-Csv

если вы вызовете $csv.IP, вы получите массив всех свойств IP:

PS> $csv.IP
1.1.1.1
1.1.1.2
1.1.1.3
1.1.1.4

и то же самое для $csv.Port:

PS> $csv.Port
22
25
80

(обратите внимание, что вывод «Порт» содержит 4 элемента — в четвертом индексе есть непечатаемый $null)

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

# use member-access enumeration to get ip and port arrays,
# and filter out $null or empty string values
$ips   = $csv.IP | where-object { $_ }
$ports = $csv.Port | where-object { $_ }

foreach( $ip in $ips )
{
    foreach( $port in $ports )
    {

        "{0} = {1}" -f $ip, $port
        Test-NetConnection -ComputerName $ip -Port $port -InformationLevel Detailed

    }
}

Если у вас есть 1 IP и 1 порт на строку CSV, можно упростить код, переименовав столбец IP в ComputerName:

Имя компьютера Порт 1.1.1.1 22 1.1.1.2 25 1.1.1.3 80

Затем, поскольку оба параметра (-ComputerName и -Port) принимают входные данные конвейера по имени свойства, вы можете передать выходные данные из Import-Csv непосредственно в Test-NetConnection:

$csv_path = Read-Host -Prompt "Enter file path"
Import-Csv -Path $csv_path | Test-NetConnection -InformationLevel Detailed

Другой вариант, если вы хотите иметь несколько портов в строке с разделителем, например точкой с запятой:

ИП Порт 1.1.1.1 22; 80 1.1.1.2 25; 444 1.1.1.3 80

Вы можете разделить точку с запятой на свойстве .Port и использовать ту же логику, что и выше, создать по одному объекту для каждого порта со свойствами, которые можно привязать из конвейера:

Import-Csv -Path $csv_path | ForEach-Object {
    foreach ($port in $_.Port -split '\s*;\s*') {
        [pscustomobject]@{
            ComputerName = $_.IP
            Port         = $port
        }
    }
} | Test-NetConnection -InformationLevel Detailed

Используя этот (многофункциональный) JoinModule (см. также: https://github.com/iRon7/Join-Object):

# "Import-Csv -Path $csv_path"
# Using your question data for a full Minimal, Reproducible Example:
Install-Script -Name Read-HtmlTable
$Data = Read-HtmlTable https://stackoverflow.com/q/78822669/1701026 -Table 0
Install-Module -Name JoinModule
$Data.Ip | CrossJoin $Data.Port | ForEach-Object { "{0} = {1}" -f @($_) }
1.1.1.1 = 22
1.1.1.1 = 25
1.1.1.1 = 80
1.1.1.1 =
1.1.1.2 = 22
1.1.1.2 = 25
1.1.1.2 = 80
1.1.1.2 =
1.1.1.3 = 22
1.1.1.3 = 25
1.1.1.3 = 80
1.1.1.3 =
1.1.1.4 = 22
1.1.1.4 = 25
1.1.1.4 = 80
1.1.1.4 =

Если вы хотите пропустить пустой Port:

$Ports = $Data.Port | Where-Object { $_ }
$Data.Ip | CrossJoin $Ports | ForEach-Object { "{0} = {1}" -f @($_) }
1.1.1.1 = 22
1.1.1.1 = 25
1.1.1.1 = 80
1.1.1.2 = 22
1.1.1.2 = 25
1.1.1.2 = 80
1.1.1.3 = 22
1.1.1.3 = 25
1.1.1.3 = 80
1.1.1.4 = 22
1.1.1.4 = 25
1.1.1.4 = 80