Вызов второй script с аргументами из script
У меня есть скрипт, который читает файл конфигурации, который приводит к набору пар имя-значение, которые я хотел бы передать в качестве аргументов функции во втором скрипте 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.
Второй вызов работает просто отлично, но одно существенное отличие состоит в том, что первая версия использует аргументы, пути которых содержат пробелы, а вторая - нет. Я неправильно обращаюсь с наличием пробелов в этих путях?
Ответы
Ответ 1
Ага. Это оказалось простой проблемой существования пробелов на пути к script.
Изменение строки Invoke-Expression:
Invoke-Expression "& `"$scriptPath`" $argumentList"
... было достаточно, чтобы заставить его начать. Благодаря Neolisk за вашу помощь и отзывы!
Ответ 2
Invoke-Expression
должен работать отлично, просто убедитесь, что вы используете его правильно. Для вашего случая это должно выглядеть так:
Invoke-Expression "$scriptPath $argumentList"
Я тестировал этот подход с помощью Get-Service и, похоже, работал как ожидалось.
Ответ 3
На самом деле проще:
Метод 1:
Invoke-Expression $scriptPath $argumentList
Метод 2:
& $scriptPath $argumentList
Способ 3:
$scriptPath $argumentList
Если у вас есть пробелы в scriptPath
, не забудьте избежать их `"$scriptPath`"
Ответ 4
Вот ответ, охватывающий более общий вопрос о вызове другого PS script из PS script, как вы можете это сделать, если бы вы составляли ваши скрипты из многих небольших узкоспециальных скриптов.
Я нашел, что это просто случай использования точек доступа. То есть, вы просто делаете:
# This is Script-A.ps1
. ./Script-B.ps1 -SomeObject $variableFromScriptA -SomeOtherParam 1234;
Я нашел все Q/A очень запутанным и сложным и в конечном итоге приземлился на простой метод выше, который действительно так же, как вызов другого script, как если бы он был функцией в исходном script, который, как мне кажется, найти более интуитивно понятный.
Dot-sourcing может "импортировать" другой script целиком, используя:
. ./Script-B.ps1
Теперь, как если бы эти два файла были объединены.
В конечном счете, я действительно не понял, что я должен создавать модуль многоразовых функций.
Ответ 5
Мы можем использовать сплаттинг для этого:
& $command @args
где @args
(автоматическая переменная $ args) разделена на массив параметров.
Под PS, 5.1
Ответ 6
Я попробовал принятое решение использования командлета 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 не имеет параметра "Script" или "ArgumentList".
Ответ 7
Вы можете выполнить его так же, как SQL-запрос.
сначала создайте свою команду/Выражение и сохраните в переменной и выполните/вызовите.
$command = ".\yourExternalScriptFile.ps1" + " -param1 '$paramValue'"
Это довольно вперед, я не думаю, что ему нужны объяснения.
Итак, все настроено на выполнение вашей команды сейчас,
Invoke-Expression $command
Я бы рекомендовал поймать исключение здесь