Как сценарий Windows PowerShell может передать свои параметры другому вызову сценария? - PullRequest
2 голосов
/ 29 марта 2012

У меня есть сценарий PowerShell, который использует пространство имен System.Data.OleDb для чтения данных из книги Excel. Поскольку 64-разрядная реализация поставщика Microsoft Jet отсутствует, сценарий необходимо запустить из 32-разрядного сеанса PowerShell. Вместо простого сбоя скрипта с сообщением об ошибке, если он запускается из 64-битного сеанса, я бы хотел, чтобы 64-битный сеанс вызывал скрипт и получал результаты из 32-битного сеанса. Я обнаружил, что это можно сделать с помощью командлета Start-Job с переключателем -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;
}

... но когда я запускаю .\Test.ps1 'string' 12345 из 64-битного сеанса, он отображает ...

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-эквивалент того, как пакетный файл может перезапустить себя с теми же параметрами ...

"%~dpnx0" %*

... где "%~dpnx0" расширяется до абсолютного пути скрипта, а %* расширяется до списка параметров. Есть простой способ сделать это? Или, по крайней мере, способ, который работает?

Ответы [ 2 ]

2 голосов
/ 29 марта 2012

Возможно, это будет выглядеть немного странно, но:

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
}
1 голос
/ 30 марта 2012

Вы можете перезапустить ваш скрипт программно как 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"
...