Как получить все объекты сразу при использовании блоков Begin/Process/End?

Я застрял в учебном проекте, который я начал, цель здесь заключалась в том, чтобы я изучил всю эту концепцию блоков Begin, Process и End.

Мой проект состоит из функции, которая позволит пользователю открыть одно окно Firefox с определенным профилем и, при желании, открыть одну или несколько веб-страниц через .url файлы, сохраненные на C: диске. Это то, что у меня есть до сих пор:

Function Ask-Web{
    [CmdletBinding()]
    Param(
        [Parameter(Position = 1)]
        [ArgumentCompletions('andy','andrew','halley', "hanna")]
        [Array]$FirefoxProfile = 'Andy',
        [Parameter(ValueFromPipeline, Position = 2)]
        [ArgumentCompletions('DIYhome','DIYreddit','DIYexchange','DIYall', 'SelectWithFZF')]
        [Array]$Pages
    )
Begin{
    $FavouritePages = [ordered]@{
    DIYhome     = "https://www.diyUK.com"
    DIYreddit   = "https://www.reddit.com/r/DIY/submit"
    DIYexchange = "https://diy.stackexchange.com/questions/ask"
    # DIYall  Will handle this later
    } 
    
    [array]$Pages = If($Pages -eq "SelectWithFZF"){Get-ChildItem -Path "C:\temp\Ask Pages\" -Filter *.url*| FZF}Else{$FavouritePages."$Pages"}  ;the FZF utility will store all selected items into $Pages variable
    }
End{. 'C:\Program Files\Mozilla Firefox\firefox.exe' -p $FirefoxProfile $Pages}

}


Предполагается, что функция позволяет пользователю выбирать страницы с помощью завершения аргументов, предварительно предоставляя веб-страницу из конвейера или делая выбор из папки C:\temp\Ask Pages\ с помощью утилиты FZF.

Во всех случаях ожидается, что пользователь предоставит одну или несколько страниц/URL-адресов:

Ask-Web -FirefoxProfile andy -Pages DIYhome,DIYreddit,DIYexchange    ; Open a singe Firefox window, with profile "Andy" and the pages DIYhome,DIYreddit and DIYexchange
Ask-Web -FirefoxProfile hanna -Pages DIYhome                         ; Open a singe Firefox window, with profile "Hanna" and the page DIYhome
Ask-Web -FirefoxProfile andrew -Pages SelectWithFZF                  ; Open a singe Firefox window, with profile "Andrew" and all the pages that were selected with FZF
Get-ChildItem .\Diy -filter *.url* | Ask-Web -FirefoxProfile hanna   ; Open a singe Firefox window, with profile "Hanna" and with all the pages provided by the pipeline

Где я, кажется, застрял, так это в том, чтобы заставить PowerShell предоставить мне все входные данные конвейера сразу, а не по одному или последний элемент конвейера, что я получаю с помощью вышеуказанной функции.

Если я выберу Process{. 'C:\Program Files\Mozilla Firefox\firefox.exe' -p $FirefoxProfile $Pages}, то PowerShell откроет окно Firefox для каждого элемента в $Pages, но с End{. 'C:\Program Files\Mozilla Firefox\firefox.exe' -p $FirefoxProfile $Pages} открывается только последний элемент в $Pages.

Я пытался:

Function Ask-Web{
    [CmdletBinding()]
    Param(
        [Parameter(Position = 1)]
        [ArgumentCompletions('andy','andrew','halley', "hanna")]
        [Array]$FirefoxProfile = 'Andy',
        [Parameter(ValueFromPipeline, Position = 2)]
        [ArgumentCompletions('DIYhome','DIYreddit','DIYexchange','DIYall', 'SelectWithFZF')]
        [Array]$Pages
    )
    Process{[Array]$Pages += @($input)}  
    End{$Pages}
}

С Ask-Web -FirefoxProfile andy -Pages DIYhome,DIYreddit,DIYexchange в блоке End я получаю:

DIYhome
DIYreddit
DIYexchange

Но с 'DIYhome','DIYreddit','DIYexchange'|Ask-Web -FirefoxProfile andy в блоке End я получаю:

DIYexchange
DIYexchange

Если я откажусь от всех блоков Begin, Process и End с помощью:

Function Ask-Web{
    [CmdletBinding()]
    Param(
        [Parameter(Position = 1)]
        [ArgumentCompletions('andy','andrew','halley', "hanna")]
        [Array]$FirefoxProfile = 'Andy',
        [Parameter(ValueFromPipeline, Position = 2)]
        [ArgumentCompletions('DIYhome','DIYreddit','DIYexchange','DIYall', 'SelectWithFZF')]
        [Array]$Pages
    )
    $input
}

Я получаю стабильные результаты, все элементы массива $Pages передаются сразу, независимо от того, как пользователь их предоставил. Как мне сделать то же самое, но с блоками Begin, Process и End?

Любая помощь будет принята с благодарностью!

Tbh, ваш вариант использования может быть не лучшим примером для обучения конвейеру, но если вы действительно хотите продолжить его, вам следует инициализировать новую локальную переменную в вашем блоке BEGIN - например, $items = @(), накапливайте элементы конвейера в своем блоке PROCESS (например, $items += $_), а затем сделайте что-то один раз со всеми элементами в вашем блоке END. Обратите внимание, что вам следует избегать += для массивов в производственном коде из соображений производительности, но, вероятно, это подойдет для вашего упражнения…

mclayton 10.04.2023 19:05
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
1
68
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

У вас есть два варианта, рекомендуемый — собрать все входные данные, прошедшие через конвейер, с помощью List<T>, это делается в блоке process вашей функции. Затем, после того, как все входные данные конвейера будут собраны, вы можете запустить Firefox в своем блоке end.

Основная причина использования List<T> для сбора входных данных конвейера, а не System.Array (@() и +=), заключается в том, что массивы имеют фиксированный размер , и PowerShell должен воссоздавать его каждый раз, когда мы добавляем к нему новый элемент, что делает его очень неэффективным. . См. этот ответ и Рекомендации по производительности сценариев PowerShell — добавление массива для получения более подробной информации об этом.

function Test-Pipeline {
    [CmdletBinding()]
    Param(
        [Parameter(ValueFromPipeline)]
        [Array] $InputObject
    )

    begin {
        $list = [System.Collections.Generic.List[object]]::new()
    }
    process {
        $list.AddRange($InputObject)
    }
    end {
        if ($list.Count) {
            # do stuff here with `$list`
            $list.ToArray()
        }
    }
}

Get-ChildItem | Test-Pipeline

List<T> в этом случае необходим, потому что PowerShell будет передавать один объект за раз через конвейер, это то, что известно как обработка по одному . Использование $input в этом случае не рекомендуется, потому что 1. ваша функция является расширенной и использование этой автоматической переменной не рекомендуется в расширенных функциях. 2. Использование $input будет означать, что ваша функция работает только из конвейера, только если вы не обойдете ее.

Второй вариант, и не рекомендуемый, — передать результат Get-ChildItem через конвейер без его перечисления. Для этого вы можете использовать Write-Output -NoEnumerate или оператор запятой ,:

function Test-Pipeline {
    [CmdletBinding()]
    Param(
        [Parameter(ValueFromPipeline)]
        [Array] $InputObject
    )

    end {
        $InputObject
    }
}

# option 1:
Write-Output (Get-ChildItem) -NoEnumerate | Test-Pipeline
# option 2:
, (Get-ChildItem) | Test-Pipeline

Ваша проблема в том, что у вас есть проблема с областью действия вашей переменной $Pages.

При прохождении через конвейер переменная $Pages сбрасывается до последнего значения перечисления. Вот почему вы получаете последнее значение только дважды. По сути, блок Process каждый раз видит новый $Pages, поэтому в последнем раунде вы добавляете к нему последний $input.

Я думаю, что самое простое решение — ввести новую переменную, которую затем использовать в блоке End:

Function Ask-Web{
    [CmdletBinding()]
    Param(
        [Parameter(Position = 1)]
        [ArgumentCompletions('andy','andrew','halley', "hanna")]
        [Array]$FirefoxProfile = 'Andy',
        [Parameter(ValueFromPipeline, Position = 2)]
        [ArgumentCompletions('DIYhome','DIYreddit','DIYexchange','DIYall', 'SelectWithFZF')]
        [Array]$Pages
    )
    Process{
        write-host "Process: $Pages"
        [Array]$AllPages += $Pages
        }  
    End{
        
        $AllPages }
}

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