Как мне получить параметр массива, который принимает входные данные из аргументов или конвейера в Powershell? - PullRequest
5 голосов
/ 15 сентября 2011

Я пытаюсь написать функцию Powershell, которая принимает аргумент массива.Я хочу, чтобы он вызывался с массивом либо как аргумент, либо как вход конвейера.Итак, вызов выглядит примерно так:

my-function -arg 1,2,3,4
my-function 1,2,3,4
1,2,3,4 | my-function

Достаточно просто получить первые два:

function my-function {
    param([string[]]$arg)
    $arg
}

Для ввода в конвейер, однако, это сложнее.С помощью ValueFromPipeline легко получить аргументы по одному в блоке процесса, но это означает, что переменная $ args - это одно значение с входом конвейера, но используется массив, если используется -args.Я могу использовать $ input в блоке END, но он вообще не получает ввода -args, а использование $ args в блоке END получает только конечный элемент из конвейера.

Я полагаю, что могуСделайте это, явно собирая значения аргументов из конвейера, используя блоки begin / process / end, следующим образом:

function my-function {
    param([Parameter(ValueFromPipeline=$true)][string[]]$args)

    begin {
        $a = @()
    }
    process {
        $a += $args
    }

    end {
        # Process array here
        $a -join ':'
    }
}

Но это кажется очень грязным.Это также кажется мне довольно распространенным требованием, поэтому я ожидал, что его легко реализовать.Есть ли более простой способ, который я пропустил?Или, если нет, есть ли способ инкапсулировать обработку аргументов в подфункцию, чтобы мне не приходилось включать все это в каждую функцию, которую я хочу работать следующим образом?

Мое конкретное требованиечто я пишу сценарии, которые принимают команды SQL в качестве входных данных.Поскольку SQL может быть многословным, я хочу разрешить возможность использования команды в конвейере (возможно, сгенерированной другой командой или из get-contents в файле), но также разрешить использование формы аргумента для быстрого оператора SELECT.Поэтому я получаю серию строк из конвейера или в качестве параметра.Если я получу массив, я просто хочу объединить его с "n", чтобы сделать одну строку - обработка строки за строкой не подходит.

Я думаю, другой вопрос будет, есть ли лучший дизайн длямой сценарий, который делает получение многострочного ввода, как этот очиститель?


Спасибо - хитрость в том, чтобы НЕ использовать ValueFromPipeline тогда ...

Причина, по которой у меня было так много проблем с получениемвсе, что я хотел, работало так, что в моих тестовых сценариях я использовал $ args в качестве имени моей аргументной переменной, забывая, что это автоматическая переменная.Так что все работало очень странно ...

PS> 1,2,3,4 | ./args

PS> get-content args.ps1
param([string[]]$args)

if ($null -eq $args) { $args = @($input) }
$args -join ':'

Doh: -)

Ответы [ 3 ]

7 голосов
/ 15 сентября 2011

Используйте автоматическую переменную $input.

Если ожидается только конвейерный ввод, то:

function my-function {
    $arg = @($input)
    $arg
}

Но я часто использую этот комбинированный подход (функция, которая принимает ввод какаргумент или через конвейер):

function my-function {
    param([string[]]$arg)

    # if $arg is $null assume data are piped
    if ($null -eq $arg) {
        $arg = @($input)
    }

    $arg
}


# test
my-function 1,2,3,4
1,2,3,4 | my-function
2 голосов
/ 17 июня 2014

Вот еще один пример использования Powershell 2.0 +

Этот пример, если параметр не требуется:

function my-function {
  [cmdletbinding()]
  Param(
    [Parameter(ValueFromPipeline=$True)]
    [string[]]$Names
  )

  End {
    # Verify pipe by Counting input
    $list = @($input)
    $Names = if($list.Count) { $list } 
      elseif(!$Names) { @(<InsertDefaultValueHere>) } 
      else { @($Names) }

    $Names -join ':'
  }
}

В одном случае произошла ошибка без 'elseif'.Если для Имен не было указано значение, переменная $ Names не будет существовать и возникнут проблемы.См. Эту ссылку для объяснения.

Если это требуется, то это не должно быть настолько сложным.Теперь я всегда ссылаюсь на эту ссылку, когда пишу свои функции по каналам.

0 голосов
/ 18 марта 2018

ValueFromPipeline

Вы должны использовать конвейер (ValueFromPipeline), поскольку PowerShell специально разработан для него.

$ args

Прежде всего,нет реальной разницы между:
my-function -<ParamName> 1,2,3,4 и
my-function 1,2,3,4 (при условии, что параметр $ParamName находится на первой позиции).

Дело в том, что имя параметра $args - неудачный выбор, поскольку $args - это автоматическая переменная, поэтому ее не следует использовать для имени параметра.Почти любое другое имя (которого нет в списке автоматических переменных ) следует делать так, как в примере из Шон М. , но вместо этого вы должны реализовать свой командлет, предполагая, что он будет вызванот середины конвейера (см .: Руководства по разработке с большим энтузиазмом ).
(И если вы хотите сделать это совершенно правильно, вы должны дать единственное имя, имена параметров множественного числа должны бытьиспользуется только в тех случаях, когда значение параметра равно , всегда многоэлементному значению.)

Middle

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

Function Create-Object {
    Param([Parameter(ValueFromPipeline=$true)][String[]]$Name)
    Begin {
        $Batch = 0
        $Index = 0
    }
    Process {
        $Batch++
        $Name | ForEach {
            $Index++
            [PSCustomObject]@{'Name' = $_; 'Index' = $Index; 'Batch' = $Batch}
        }
    }
}

Он в основном создает пользовательские объекты из списка имен ($Names = "Adam", "Ben", "Carry").
Это происходит, когда вы задаете $ Names через аргумент:

Create-Object $Names

Name  Index Batch
----  ----- -----
Adam      1     1
Ben       2     1
Carry     3     1

(он перебирает все имена в параметре $Name с помощью командлета ForEach.)

И это происходит, когда вы поставляете $Names по конвейеру:

$Names | Create-Object

Name  Index Batch
----  ----- -----
Adam      1     1
Ben       2     2
Carry     3     3

Обратите внимание, что выходные данные очень похожи (если это не было для столбца batch, выходные данные на самом делето же самое), но объекты теперь создаются в 3 отдельных пакетах, что означает, что каждый элемент повторяется в методе process, а цикл ForEach повторяет только один в каждом пакете, поскольку параметр $Name содержит массив с one каждый элемент process итерация.

Вариант использования

Представление о том, что $Names исходит из медленного источника (например, другой угрозы или удаленной базы данных).Если вы используете конвейер для обработки $Names, ваш командлет может начать обработку $Names (и передать новые объекты в следующий командлет), даже если еще не все $Names доступны.По сравнению с предоставлением $Names через аргумент, все $Names необходимо будет сначала собрать, прежде чем ваш командлет обработает их и передаст новые объекты в конвейер.

...