Как расширить массив PowerShell при передаче его функции

У меня есть две функции PowerShell, первая из которых вызывает вторую. Оба они принимают N аргументов, и один из них определен для простого добавления флага и вызова другого. Вот примеры определений:

function inner
{
  foreach( $arg in $args )
    {
      # do some stuff
    }
}

function outer
{
  inner --flag $args
}

Использование будет выглядеть примерно так:

inner foo bar baz

или это

outer wibble wobble wubble

Цель состоит в том, чтобы последний пример был эквивалентен

inner --flag wibble wobble wubble

Проблема: Как здесь определено, последнее фактически приводит к тому, что в inner передаются два аргумента: первый - «--flag», а второй - это массив, содержащий «wibble», «wobble» и «wubble». Я хочу, чтобы inner получил четыре аргумента: флаг и три исходных аргумента.

Поэтому мне интересно, как убедить PowerShell расширить массив $ args перед его передачей в inner, передав его как N элементов, а не как один массив. Я считаю, что вы можете сделать это в Ruby с помощью оператора splatting (символ *), и я почти уверен, что PowerShell может это сделать, но я не помню, как это сделать.

Структурированный массив Numpy
Структурированный массив Numpy
Однако в реальных проектах я чаще всего имею дело со списками, состоящими из нескольких типов данных. Как мы можем использовать массивы numpy, чтобы...
T - 1Bits: Генерация последовательного массива
T - 1Bits: Генерация последовательного массива
По мере того, как мы пишем все больше кода, мы привыкаем к определенным способам действий. То тут, то там мы находим код, который заставляет нас...
Что такое деструктуризация массива в JavaScript?
Что такое деструктуризация массива в JavaScript?
Деструктуризация позволяет распаковывать значения из массивов и добавлять их в отдельные переменные.
7
0
12 719
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Что ж, может быть способ получше, но посмотрите, работает ли он:

inner --flag [string]::Join(" ", $args)

Похоже, это не работает напрямую, потому что результат string :: Join передается как единственный аргумент, в данном случае «wibble wobble wubble».

Charlie 16.01.2009 19:27

хммм ... позвольте мне еще немного покопаться

EBGreen 16.01.2009 19:30

Основываясь на идее @ EBGreen и связанном с ней вопросе, который я заметил на боковой панели, возможное решение таково:

function outer
{
    invoke-expression "inner --flag $($args -join ' ')"
}

Примечание. В этом примере используется новый оператор -join Powershell 2.0 CTP.

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

Я тоже не фанат выражения invoke, но если вы контролируете аргументы, это не страшно. Я думаю, что CTP2 или CTP3 также предоставляют токенизатор, и в этом случае вы можете дезинфицировать аргументы перед вызовом.

EBGreen 16.01.2009 19:38

Вы можете сделать это только с PowerShell v1.0: invoke-expression "inner --flag $ args" По умолчанию массив будет объединен пробелами (есть также способ изменить символ по умолчанию, но я могу '' не помню).

JasonMArcher 16.02.2009 06:03

Ага, ты прав насчет этого. Разделитель управляется переменной $ OFS.

Charlie 17.02.2009 02:13
Ответ принят как подходящий

В PowerSHell V1 нет хорошего решения этой проблемы. В V2 мы добавили сплаттинг (хотя по разным причинам для этой цели мы используем @ вместо *). Вот как это выглядит:

PS (STA-ISS) (2)> function foo ($ x, $ y, $ z) {"x: $ x y: $ y z: $ z"}

PS (STA-ISS) (3)> $ a = 1,2,3

PS (STA-ISS) (4)> foo $ a # передается как единственный аргумент

x: 1 2 3 y: z:

ПС (STA-ISS) (5)> foo @a # splatted

х: 1 г: 2 г: 3

Какая честь получить ответ от одного из гуру PowerShell! У меня установлен PowerShell 2 CTP2, поэтому я попробую.

Charlie 24.01.2009 04:04

@Charlie @BrucePayette У меня System.Object[] в & someexefile.exe $args под Powershell v5.1. Когда я использую & someexefile.exe @args, он не работает с символами Unicode (chcp 65001 готов). Как передавать аргументы между скриптами ps1?

Artyom 27.04.2017 12:29

@Artyom хм хороший вопрос. Чем не работает? Какие ошибки вы видите?

Charlie 03.05.2017 18:53

@Charlie Ошибка заключалась в том, что переданным аргументом был "System.Object []", а не мои сглаженные параметры.

Artyom 03.05.2017 20:16

Если вам нужно быстрое готовое решение, вы можете скопировать мою пасту:

<#
    .SYNOPSIS
    Asks a question and waits for user's answer

    .EXAMPLE
    Usage with shortcuts and without ReturnValue
    Invoke-Question -Question "What would you like" -Answers "&Eggs", "&Toasts", "&Steak"

    Shows the quesiton and waits for input. Let's assume user input is 'S', the return value would be 2 (index of "&Steak")

    .EXAMPLE
    Usage without shortcuts and with ReturnValue
    Invoke-Question -Question "What would you like" -Answers "Eggs", "Toasts", "Steak" -ReturnValue

    Shows the quesiton and waits for input. The answers are prefixed with numbers 1, 2 and 3 as shortcuts.
    Let's assume user input is 2, the return value would be "Toasts" (prefixed numbers are "index + 1")

    .EXAMPLE
    Usage from pipeline with default value
    @("Eggs", "Toasts", "Steak") | Invoke-Question -Question "What would you like" -ReturnValue -Default 2

    Shows the quesiton and waits for input. The answers are taken from pipeline and prefixed with numbers 1, 2 and 3 as shortcuts.
    Steak is marked as default. If user simply continues without a choice, Steak is chosen for her.
    However, let's assume user input is 1, the return value would be "Eggs" (prefixed numbers are "index + 1")
#>
function Invoke-Question {
    [CmdletBinding()]
    param(
        # Main question text
        [Parameter(Mandatory = $true)]
        [string] $Question,

        # Question description, e.g. explanation or more information
        [Parameter(Mandatory = $false)]
        [string] $Description = "",

        # Default answer as index in the array, no answer is selected by default (value -1)
        [Parameter(Mandatory = $false)]
        [int] $Default = -1,

        # Set of answers, if the label is given with & sign, the prefixed letter is used as shortcut, e.g. "&Yes" -> Y,
        # otherwise the answer is prefixed with "index + 1" number as a shortcut
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [string[]] $Answers,

        # If set, returns a value of selected answer, otherwise returns its index in the Answer array
        [switch] $ReturnValue
    )

    begin {
        # init choices
        $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription]
        $answerNumber = 1
        $rememberAnswers = @()
    }

    process {
        #init answers
        foreach ($answer in $answers) {
            $rememberAnswers += $answer
            if ($answer -notmatch "&") {
                # add number if shortcut not specified
                $answer = "&$answerNumber $answer"
            }

            $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList $answer))
            $answerNumber++
        }
    }

    end {
        # ask question and return either value or index
        $index = $Host.UI.PromptForChoice($Question, $Description, $choices, $Default)
        if ($ReturnValue) {
            $rememberAnswers[$index]
        } else {
            $index
        }
    }
}

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