Вызвать второй скрипт с аргументами из скрипта
Я относительно новичок в PowerShell и у меня есть скрипт, который читает файл конфигурации, который приводит к набору пар имя-значение, которые я хотел бы передать в качестве аргументов функции во втором скрипте PowerShell.
Я не знаю, какие параметры будут помещены в этот файл конфигурации во время разработки, поэтому в тот момент, когда мне нужно вызвать этот второй сценарий PowerShell, у меня в основном только одна переменная, которая имеет путь к этому второму сценарию, и вторая переменная, которая представляет собой массив аргументов для передачи в сценарий, указанный в переменной пути.
Таким образом, переменная, содержащая путь ко второму сценарию ($scriptPath), может иметь значение вроде:
"c:\the\path\to\the\second\script.ps1"
Переменная, содержащая аргументы ($argumentsList), может выглядеть примерно так:
-ConfigFilename "doohickey.txt" -RootDirectory "c:\some\kind\of\path" -Max 11
Как мне перейти от этого положения дел к выполнению script.ps1 со всеми аргументами из $ argumentsList?
Мне бы хотелось, чтобы все команды 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
... а вот макет вызывающего скрипта в файле с именем invokee.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.
Второй вызов работает просто отлично, но одно существенное отличие состоит в том, что первая версия использует аргументы, пути которых содержат пробелы, а вторая - нет. Я неправильно обращаюсь с наличием пробелов в этих путях?
7 ответов
Ага. Это оказалось простой проблемой наличия пробелов на пути к сценарию.
Изменение строки Invoke-Expression на:
Invoke-Expression "& `"$scriptPath`" $argumentList"
... было достаточно, чтобы начать. Спасибо Neolisk за вашу помощь и обратную связь!
Invoke-Expression
должно работать идеально, просто убедитесь, что вы используете его правильно. Для вашего случая это должно выглядеть так:
Invoke-Expression "$scriptPath $argumentList"
Я проверил этот подход с Get-Service и, похоже, работает как положено.
Гораздо проще на самом деле:
Способ 1:
Invoke-Expression $scriptPath $argumentList
Способ 2:
& $scriptPath $argumentList
Способ 3:
$scriptPath $argumentList
Если у вас есть пробелы в вашем scriptPath
не забудь сбежать от них `"$scriptPath`"
Мы можем использовать сплаттинг для этого:
& $command @args
где @args
( автоматическая переменная $ args) разделена на массив параметров.
Под PS, 5.1
Вот ответ, охватывающий более общий вопрос вызова другого сценария PS из сценария PS, что вы можете сделать, если составляете свои сценарии из множества небольших сценариев узкого назначения.
Я обнаружил, что это просто случай использования точечного поиска. То есть вы просто делаете:
# This is Script-A.ps1
. ./Script-B.ps1 -SomeObject $variableFromScriptA -SomeOtherParam 1234;
Я обнаружил, что все вопросы и ответы очень запутаны и сложны, и в конечном итоге они остановились на простом методе, описанном выше, который на самом деле похож на вызов другого скрипта, как если бы это была функция в оригинальном скрипте, который, как мне кажется, кажется более интуитивным.
Dot-Sourcing может "импортировать" другой скрипт полностью, используя:
. ./Script-B.ps1
Теперь, как будто два файла объединены.
В конечном счете, я действительно упустил идею, что мне следует создать модуль многократно используемых функций.
Я попробовал общепринятое решение использовать командлет 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".
Вы можете выполнить его так же, как запрос SQL. сначала соберите вашу команду / выражение и сохраните в переменной и выполните /invoke.
$command = ".\yourExternalScriptFile.ps1" + " -param1 '$paramValue'"
Это довольно вперед, я не думаю, что это нуждается в объяснениях. Итак, все готово для выполнения вашей команды сейчас,
Invoke-Expression $command
Я бы порекомендовал поймать исключение здесь
Я предполагаю, что вы хотите запустить файл.ps1 [здесь $scriptPath вместе с несколькими аргументами, хранящимися в $argumentList] из другого файла.ps1
Invoke-Expression "& $scriptPath $argumentList"
Этот фрагмент кода будет работать нормально