【问题标题】:How can a Windows PowerShell script pass its parameters through to another script invocation?Windows PowerShell 脚本如何将其参数传递给另一个脚本调用?
【发布时间】:2026-01-29 19:40:01
【问题描述】:

简而言之,我只是在寻找 PowerShell 等价物,即批处理文件如何调用具有相同参数的脚本...

"%~dpnx0" %*

...其中"%~dpnx0" 扩展为脚本的绝对路径,%* 扩展为参数列表。有没有一种简单的方法可以复制%*?或者,至少,一种可行的方式?

我有一个 PowerShell 脚本,它使用 System.Data.OleDb 命名空间从 Excel 工作簿中读取数据。因为没有 Microsoft Jet 提供程序的 64 位实现,所以脚本需要从 32 位 PowerShell 会话运行。如果脚本是从 64 位会话运行的,则不是简单地让脚本失败并显示错误消息,而是让 64 位会话调用脚本并从 32 位会话中检索结果。我发现这可以使用Start-Job cmdlet-RunAs32 开关来完成,但是我无法为-ArgumentList 参数提供值。

我想出了以下方法来搜索具有值的脚本参数并从中构建命令行:

function GetValueText([Object] $value)
{
    [String] $text = if ($value -eq $null) {
        '$null';
    } elseif ($value -is [String]) {
        "'$value'";
    } elseif ($value -is [Array]) {
        '@({0})' -f (($value | ForEach-Object { GetValueText $_ }) -join ', ');
    } else {
        "$value";
    }

    return $text;
}

if ([IntPtr]::Size -gt 4)
{
    [String] $scriptPath = $MyInvocation.MyCommand.Path;
    [String[]] $parameters = @(
        $MyInvocation.MyCommand.Parameters.Keys `
        | ForEach-Object {
            [Object] $parameterValue = Get-Variable -Name $_ -ValueOnly;

            if ($parameterValue -ne $null)
            {
                [String] $parameterValueText = GetValueText $parameterValue;

                '-{0}' -f $_;
                $parameterValueText;
            }
        }
    );
    [Object] $job = Start-Job -FilePath $scriptPath -RunAs32 -ArgumentList $parameters;
    [Object[]] $data = $job | Wait-Job | Receive-Job;

    $data;
}
else
{
    # Retrieve data...
}

当它到达Start-Job 行时,它会生成带有消息"Cannot convert value "-Argument1" to type "System.Int32[]"" 的错误。 -Argument1 是脚本的第一个参数,类型为[Int32[]],那么这是否意味着-ArgumentList 只适用于位置参数而不是命名参数?

我也尝试将其简化为...

param(
    [String] $stringArg,
    [Int32] $int32Arg
)

$PSBoundParameters;

if ([IntPtr]::Size -gt 4)
{
    [String] $scriptPath = $MyInvocation.MyCommand.Path;
    [Object] $job = Start-Job -FilePath $scriptPath -RunAs32 -ArgumentList @PSBoundParameters;

    $job | Wait-Job | Receive-Job;
}
else
{
    Get-Date;
}

...但是当我从 64 位会话运行 .\Test.ps1 'string' 12345 时,它会显示...

Key                                                         Value
---                                                         -----
stringArg                                                   string
int32Arg                                                    12345
Start-Job : Missing an argument for parameter 'ArgumentList'. Specify a parameter of type 'System.Object[]' and try again.
At X:\Test.ps1:11 char:72
+     [Object] $job = Start-Job -FilePath $scriptPath -RunAs32 -ArgumentList <<<<  @PSBoundParameters;
    + CategoryInfo          : InvalidArgument: (:) [Start-Job], ParameterBindingException
    + FullyQualifiedErrorId : MissingArgument,Microsoft.PowerShell.Commands.StartJobCommand

...所以@PSBoundParameters 似乎评估为$null。我不确定为什么这不起作用或还有什么可以尝试的。

【问题讨论】:

    标签: powershell parameters parameter-passing powershell-2.0 32bit-64bit


    【解决方案1】:

    这可能看起来有点奇怪,但是:

    param(
    [String] $stringArg,
    [Int32] $int32Arg
    )
    
    if ([IntPtr]::Size -gt 4)
    {
    [String] $scriptPath = $MyInvocation.MyCommand.Path;
    
    $params = @()
    $psboundparameters.keys |
      foreach {
          $params += "-$($_)"
          $params +=  $psboundparameters.$_
          }
    
    
    $sb = [scriptblock]::Create(@"
    &'$scriptpath' $params
    "@)
    
    [Object] $job = Start-Job -scriptblock $sb -RunAs32 
    $job | Wait-Job | Receive-Job;
    }
    else
    {
    Get-Date
    }
    

    【讨论】:

    • 感谢您的回复。如果脚本路径中有空格,则会失败,但我可以通过将"&amp; '$scriptpath' $(&amp;{$args} @psboundparameters)" 传递给[scriptblock]::Create() 来修复它。进行该更改后,该脚本在不带参数的情况下调用时有效,但如果我运行.\Test.ps1 'string' 12345,它会在Receive-JobCannot process argument transformation on parameter 'int32Arg'. Cannot convert value "string" to type "System.Int32". Error: "Input string was not in a correct format." 失败你能解释一下这是在做什么吗?尤其是&amp;{$args},让我很困惑。
    • 这将允许您传递命名参数。散列散列表会从散列表的键/值对中生成一组“参数值”字符串。但是你不能单独评估一个splat。 &{$args} 提供一个脚本块来接受将回显“-parameter value”字符串的 splat,然后它们将包含在传递给作业的脚本块中。
    • 我无法重现您的结果。在我的系统(W7 x64)上,它返回日期,运行 get-job 显示创建的作业仍然存在,状态为“已完成”。
    • 我也在 64 位 Windows 7 上。我尝试了您更新的脚本,.\Test.ps1.\Test.ps1 123.\Test.ps1 123 456.\Test.ps1 123 456 foo.\Test.ps1 -int32Arg 123 都可以正常工作。 .\Test.ps1 foo.\Test.ps1 foo 123.\Test.ps1 -stringArg foo 以及前两个参数无法转换为整数的任何其他内容都失败并出现错误Cannot process argument transformation on parameter 'int32Arg'. Cannot convert value "foo" to type "System.Int32". 我不明白为什么它试图将这些标记与第二个位置参数匹配.
    • 我也不明白。将该脚本运行为: PS C:\scripts\test\test scripts> ./test.ps1 'string' 12345 或 PS C:\scripts\test\test scripts> ./test.ps1 -stringarg 'string' -int32arg 12345正如我的系统所预期的那样,两者都返回日期。注意 - 这是来自 ps 控制台,而不是 ISE。
    【解决方案2】:

    您可以像这样以编程方式将脚本重新启动为 32 位进程:

    [cmdletbinding()]
    param(
        [parameter(Mandatory=$true)]
        [String] $stringArg,
        [parameter(Mandatory=$true)]
        [Int32] $int32Arg
    )
    
    if ([IntPtr]::Size -ne 4) {
        $p = $PSBoundParameters
        $32bitPs = 'C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe'
        $myArgs = @($MyInvocation.MyCommand.Path)
        $myArgs += ( $p.Keys | % { ('-' + $_), $p.Item($_) } )
        Start-Process -NoNewWindow -FilePath $32bitPs -ArgumentList $myArgs
        exit
    }
    
    Write-Host "Hi I'm a 32 bit process, my args are:"
    $PSBoundParameters
    # Do 32 bit stuff here...
    Read-Host "Press enter to quit"
    

    【讨论】:

    • 这是我最初想到的方式,并且对于纯粹写入控制台、文件等而不向管道产生任何数据的脚本可以正常工作,但我意识到任何检索到的数据将卡在脚本的 32 位实例中,没有(简单?)方法将其返回到父 64 位实例。如果将Get-Date 添加到脚本的末尾,当直接从 32 位会话运行时,您会在管道中看到它,但从 64 位会话中它会丢失。我希望 64 位处理是透明的,这样我仍然可以将此脚本通过管道传输到 cmdlet。
    • 如果您必须将两者混合使用,那么重新启动整个过程将无法正常工作,因此我们会创建一个后台作业会更好。如果一切都作为 32 位脚本运行,那么您可以以这种方式运行所有代码。
    最近更新 更多