Вызов второго скрипта с аргументами из скрипта

У меня есть сценарий, который считывает файл конфигурации, в результате чего получается набор пар "имя-значение", которые я хотел бы передать в качестве аргументов функции во втором сценарии PowerShell.

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

Таким образом, переменная, содержащая путь ко второму сценарию ($scriptPath), может иметь такое значение, как:

"c:\the\path\to\the\second\script.ps1"

Переменная, содержащая аргументы ($argumentList), может выглядеть примерно так:

-ConfigFilename "doohickey.txt" -RootDirectory "c:\some\kind\of\path" -Max 11

Как мне перейти от такого положения дел к выполнению script.ps1 со всеми аргументами из $ argumentList?

Я бы хотел, чтобы любые команды write-host из этого второго скрипта были видны консоли, из которой вызывается этот первый скрипт.

Я пробовал использовать точечный источник, Invoke-Command, Invoke-Expression и Start-Job, но не нашел подхода, который бы не приводил к ошибкам.

Например, я подумал, что проще всего сначала попробовать Start-Job, который называется следующим образом:

Start-Job -FilePath $scriptPath -ArgumentList $argumentList

... но это не сработает с этой ошибкой:

System.Management.Automation.ValidationMetadataException:
Attribute cannot be added because it would cause the variable
ConfigFilename with value -ConfigFilename to become invalid.

... в этом случае ConfigFilename является первым параметром в списке параметров, определенном вторым скриптом, и мой вызов, по-видимому, пытается установить его значение на -ConfigFilename, которое, очевидно, предназначено для идентификации параметра по имени, а не для установки его ценить.

Что мне не хватает?

РЕДАКТИРОВАТЬ:

Хорошо, вот макет вызываемого скрипта в файле с именем invokee.ps1

Param(
[parameter(Mandatory=$true)]
[alias("rc")]
[string]
[ValidateScript( {Test-Path $_ -PathType Leaf} )]
$ConfigurationFilename,
[alias("e")]
[switch]
$Evaluate,
[array]
[Parameter(ValueFromRemainingArguments=$true)]
$remaining)

function sayHelloWorld()
{
    Write-Host "Hello, everybody, the config file is <$ConfigurationFilename>."
    if ($ExitOnErrors)
    {
        Write-Host "I should mention that I was told to evaluate things."
    }
    Write-Host "I currently live here: $gScriptDirectory"
    Write-Host "My remaining arguments are: $remaining"
    Set-Content .\hello.world.txt "It worked"
}

$gScriptPath = $MyInvocation.MyCommand.Path
$gScriptDirectory = (Split-Path $gScriptPath -Parent)
sayHelloWorld

... а вот макет вызывающего скрипта в файле с именем invoker.ps1:

function pokeTheInvokee()
{
    $scriptPath = (Join-Path -Path "." -ChildPath "invokee.ps1")
    $scriptPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($scriptPath)
    
    $configPath = (Join-Path -Path "." -ChildPath "invoker.ps1")
    $configPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($configPath)
    
    $argumentList = @()
    $argumentList += ("-ConfigurationFilename", "`"$configPath`"")
    $argumentList += , "-Evaluate"

    Write-Host "Attempting to invoke-expression with: `"$scriptPath`" $argumentList"
    Invoke-Expression "`"$scriptPath`" $argumentList"
    Invoke-Expression ".\invokee.ps1 -ConfigurationFilename `".\invoker.ps1`" -Evaluate
    Write-Host "Invokee invoked."
}

pokeTheInvokee

Когда я запускаю invoker.ps1, я получаю эту ошибку при первом вызове Invoke-Expression:

Invoke-Expression : You must provide a value expression on
the right-hand side of the '-' operator.

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


person Hoobajoob    schedule 12.10.2012    source источник
comment
Также было бы полезно, если бы вы предоставили исходный код для каждого из ваших скриптов. Например, создайте проект POC (proof of concept), иллюстрирующий вашу проблему. Затем мы можем протестировать и поиграть с ним, и, возможно, кто-нибудь найдет решение или обходной путь.   -  person Neolisk    schedule 12.10.2012
comment
Да, я пытаюсь создать мини-версию, чтобы проиллюстрировать это. Скоро отправлю ...   -  person Hoobajoob    schedule 12.10.2012


Ответы (8)


Ага. Это оказалось простой проблемой из-за наличия пробелов в пути к скрипту.

Изменение строки Invoke-Expression на:

Invoke-Expression "& `"$scriptPath`" $argumentList"

... этого было достаточно, чтобы он заработал. Спасибо Neolisk за вашу помощь и отзывы!

person Hoobajoob    schedule 12.10.2012
comment
Чтобы добавить к вашему ответу. Наличие пробелов в пути означает, что вам нужно использовать кавычки. Кавычки обрабатываются Invoke-Expression как строка, а не как команда. Следовательно, если вы хотите выполнить строку, вам нужно использовать оператор вызова, который является символом амперсанда в начале вашего примера. Здесь есть определение оператора вызова. - person Aeropher; 30.03.2016
comment
Когда я пробую свой сценарий таким образом, у него есть синтаксическая ошибка с отсутствующей конечной цитатой. Метод 2 ниже работает для меня - person Michele; 23.01.2019
comment
У меня возникла проблема с символом точки с запятой в одном из строковых аргументов в моем списке аргументов скрипта. Invoke-Expression продолжал интерпретировать его как разделитель операторов и передавал скрипту только начало строки в качестве аргумента. Когда я применил этот ответ в своем коде, он работал нормально. - person Yuhis; 13.12.2019

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

Invoke-Expression "$scriptPath $argumentList"

Я протестировал этот подход с помощью Get-Service, и, похоже, он работает должным образом.

person Neolisk    schedule 12.10.2012
comment
Invoke-Expression $ scriptPath $ argumentList немедленно возвращается и, похоже, не запускает сценарий. Если я изменю его на Invoke-Expression powershell $ scriptPath $ argumentList, PowerGui просто зависнет. - person Hoobajoob; 12.10.2012
comment
@Hoobajoob: Invoke-Expression не возвращается немедленно, он терпеливо ждет, пока не завершится запущенный скрипт - я только что тестировал проект POC. Должно быть что-то не так со сценарием, на который вы нацеливаетесь. - person Neolisk; 12.10.2012
comment
Хм - я запускал этот второй сценарий разными способами из командных файлов, IDE Visual Studio, командных строк DOS и сеансов консоли PowerShell, но это первый раз, когда я пытался запустить его из другого скрипта PowerShell. Какие вещи могут быть в самом скрипте, которые мешают его успешному запуску через Invoke-Expression? - person Hoobajoob; 12.10.2012
comment
это будет работать, только если все объекты в списке аргументов имеют строковый тип, правда? - person David Klempfner; 23.03.2015
comment
@Backwards_Dave: $scriptPath и $argumentList здесь строки. Строка может содержать нестроковый аргумент (записанный как строка). Надеюсь, он ответит на ваш вопрос. - person Neolisk; 23.03.2015

На самом деле гораздо проще:

Метод 1:

Invoke-Expression $scriptPath $argumentList

Метод 2:

& $scriptPath $argumentList

Метод 3:

$scriptPath $argumentList

Если у вас есть пробелы в scriptPath, не забудьте их `"$scriptPath`"

person Mrchief    schedule 23.01.2014
comment
Спасибо за варианты, мне очень нравится дополнительная опция, предоставленная этим ответом на другой вопрос. stackoverflow.com/a/33206405/3794873 - person dragon788; 11.04.2017

Вот ответ, охватывающий более общий вопрос о вызове другого сценария PS из сценария PS, как если бы вы составляли свои сценарии из множества небольших узконаправленных сценариев.

Я обнаружил, что это просто случай использования точечного поиска. То есть вы просто делаете:

# This is Script-A.ps1

. ./Script-B.ps1 -SomeObject $variableFromScriptA -SomeOtherParam 1234;

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

Dot-sourcing может полностью "импортировать" другой скрипт, используя:

. ./Script-B.ps1

Теперь это как если бы два файла были объединены.

В конечном счете, чего мне действительно не хватало, так это идеи о том, что я должен создать модуль многоразовых функций.

person Luke Puplett    schedule 26.11.2015
comment
В чем преимущество модуля, если мне не нужно экспортировать его в другие системы? - person Blaisem; 13.05.2021

Мы можем использовать splatting для этого:

& $command @args

где @args (автоматическая переменная $ args) разбивается на массив параметров.

Под PS, 5.1

person Artyom    schedule 19.08.2017

Я попробовал принятое решение с использованием командлета Invoke-Expression, но оно не сработало для меня, потому что в моих аргументах были пробелы. Я пытался разобрать аргументы и избежать пробелов, но у меня не получалось должным образом заставить его работать, а также, на мой взгляд, это была действительно грязная работа. Итак, поэкспериментировав, я решил проблему так:

function Invoke-Script
{
    param
    (
        [Parameter(Mandatory = $true)]
        [string]
        $Script,

        [Parameter(Mandatory = $false)]
        [object[]]
        $ArgumentList
    )

    $ScriptBlock = [Scriptblock]::Create((Get-Content $Script -Raw))
    Invoke-Command -NoNewScope -ArgumentList $ArgumentList -ScriptBlock $ScriptBlock -Verbose
}

# example usage
Invoke-Script $scriptPath $argumentList

Единственным недостатком этого решения является то, что вам необходимо убедиться, что в вашем скрипте нет параметра «Script» или «ArgumentList».

person gigi    schedule 21.11.2016

Вы можете выполнить его так же, как SQL-запрос. сначала создайте свою команду / выражение, сохраните в переменной и выполните / вызовите.

$command =  ".\yourExternalScriptFile.ps1" + " -param1 '$paramValue'"

Это довольно быстро, я не думаю, что это требует объяснений. Итак, все готово к выполнению вашей команды сейчас,

Invoke-Expression $command

Я бы порекомендовал поймать здесь исключение

person UniCoder    schedule 03.08.2017

Я предполагаю, что вы хотите запустить файл .ps1 [здесь $ scriptPath вместе с несколькими аргументами, хранящимися в $ argumentList] из другого файла .ps1

Invoke-Expression "& $scriptPath $argumentList"

Этот фрагмент кода будет работать нормально

person JonSnow    schedule 31.03.2020