74

我有一个脚本,它读取一个配置文件,该文件产生一组名称值对,我想将它们作为参数传递给第二个 PowerShell 脚本中的函数。

我不知道在设计时会在这个配置文件中放置什么参数,所以就在我需要调用第二个 PowerShell 脚本的时候,我基本上只有一个变量具有第二个脚本的路径,还有一个变量变量,它是要传递给路径变量中标识的脚本的参数数组。

因此,包含第二个脚本 ( $scriptPath) 路径的变量可能具有如下值:

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

包含参数 ( $argumentList) 的变量可能类似于:

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

如何使用 $argumentList 中的所有参数从这种事态中执行 script.ps1?

我希望从调用第一个脚本的控制台可以看到来自第二个脚本的任何写入主机命令。

我尝试过点源、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.

第二个调用工作得很好,但一个显着的区别是第一个版本使用的参数的路径中有空格,而第二个没有。我是否错误地处理了这些路径中存在的空格?

4

8 回答 8

65

啊哈。原来这是一个简单的问题,即脚本路径中有空格。

将 Invoke-Expression 行更改为:

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

...足以让它开始。感谢 Neolisk 的帮助和反馈!

于 2012-10-12T03:25:56.720 回答
48

Invoke-Expression应该可以完美运行,只需确保您正确使用它。对于您的情况,它应该如下所示:

Invoke-Expression "$scriptPath $argumentList"

我使用 Get-Service 测试了这种方法,并且似乎按预期工作。

于 2012-10-12T00:52:49.267 回答
33

实际上要简单得多

方法一:

Invoke-Expression $scriptPath $argumentList

方法二:

& $scriptPath $argumentList

方法三:

$scriptPath $argumentList

如果您的 中有空格scriptPath,请不要忘记转义它们`"$scriptPath`"

于 2014-01-23T03:42:03.917 回答
13

我们可以为此使用喷溅:

& $command @args

其中@args自动变量 $args)被放入参数数组中。

在 PS 下,5.1

于 2017-08-19T14:06:52.817 回答
13

这是一个涵盖从 PS 脚本调用另一个 PS 脚本的更一般问题的答案,如果您正在编写许多小的、用途狭窄的脚本的脚本,您可能会这样做。

我发现这只是使用点源的一个案例。也就是说,您只需执行以下操作:

# This is Script-A.ps1

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

我发现所有的 Q/A 都非常混乱和复杂,最终落在了上面的简单方法上,这真的就像调用另一个脚本一样,好像它是原始脚本中的一个函数,我觉得这更直观。

点源可以“导入”整个其他脚本,使用:

. ./Script-B.ps1

现在好像这两个文件合并了。

最终,我真正缺少的是我应该构建一个可重用函数模块的概念。

于 2015-11-26T15:32:50.027 回答
2

我尝试了使用 Invoke-Expression cmdlet 的公认解决方案,但它对我不起作用,因为我的参数上有空格。我试图解析参数并转义空格,但我无法使其正常工作,而且在我看来,这确实是一项肮脏的工作。所以经过一些实验,我对这个问题的看法是:

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”参数。

于 2016-11-21T11:17:40.577 回答
1

You can execute it same as SQL query. first, build your command/Expression and store in a variable and execute/invoke.

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

It is pretty forward, I don't think it needs explanations. So all set to execute your command now,

Invoke-Expression $command

I would recommend catching the exception here

于 2017-08-03T10:19:34.287 回答
-1

我假设您想从另一个 .ps1 文件运行 .ps1 文件 [此处为 $scriptPath 以及存储在 $argumentList 中的多个参数]

Invoke-Expression "& $scriptPath $argumentList"

这段代码可以正常工作

于 2020-03-31T08:34:39.850 回答