У меня есть две функции 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 может это сделать, но я не помню, как это сделать.



Что ж, может быть способ получше, но посмотрите, работает ли он:
inner --flag [string]::Join(" ", $args)
хммм ... позвольте мне еще немного покопаться
Основываясь на идее @ EBGreen и связанном с ней вопросе, который я заметил на боковой панели, возможное решение таково:
function outer
{
invoke-expression "inner --flag $($args -join ' ')"
}
Примечание. В этом примере используется новый оператор -join Powershell 2.0 CTP.
Однако я все же хотел бы найти способ получше, поскольку это похоже на взлом и ужасно с точки зрения безопасности.
Я тоже не фанат выражения invoke, но если вы контролируете аргументы, это не страшно. Я думаю, что CTP2 или CTP3 также предоставляют токенизатор, и в этом случае вы можете дезинфицировать аргументы перед вызовом.
Вы можете сделать это только с PowerShell v1.0: invoke-expression "inner --flag $ args" По умолчанию массив будет объединен пробелами (есть также способ изменить символ по умолчанию, но я могу '' не помню).
Ага, ты прав насчет этого. Разделитель управляется переменной $ OFS.
В 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 @BrucePayette У меня System.Object[] в & someexefile.exe $args под Powershell v5.1. Когда я использую & someexefile.exe @args, он не работает с символами Unicode (chcp 65001 готов). Как передавать аргументы между скриптами ps1?
@Artyom хм хороший вопрос. Чем не работает? Какие ошибки вы видите?
@Charlie Ошибка заключалась в том, что переданным аргументом был "System.Object []", а не мои сглаженные параметры.
Если вам нужно быстрое готовое решение, вы можете скопировать мою пасту:
<#
.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
}
}
}
Похоже, это не работает напрямую, потому что результат string :: Join передается как единственный аргумент, в данном случае «wibble wobble wubble».