Cannot process argument transformation

2019-05-05 05:32发布

问题:

Inspired by this post, I created the script below DOSCommands.ps1

Function Invoke-DOSCommands {
    Param(
        [Parameter(Position=0,Mandatory=$true)]
        [String]$cmd,
        [String]$tmpname = $(([string](Get-Random -Minimum 10000 -Maximum 99999999)) + ".cmd"),
        [switch]$tmpdir = $true)
    if ($tmpdir) {
        $cmdpath = $(Join-Path -Path $env:TEMP -ChildPath $tmpname);
    }
    else {
        $cmdpath = ".\" + $tmpname
    }
    Write-Debug "tmpfile: " + $cmdpath
    Write-Debug "cmd: " + $cmd
    echo $cmd | Out-File -FilePath $cmdpath -Encoding ascii;
    & cmd.exe /c $cmdpath | Out-Null
}

Invoke-DOSCommands "Echo ""Hello World""", -tmpdir $false

However, on execution it returns this error:

Invoke-DOSCommands : Cannot process argument transformation on parameter 'cmd'. Cannot convert value to type S ystem.String.
At DOSCommands.ps1:20 char:19
+ Invoke-DOSCommands <<<<  "Echo ""Hello World""", -tmpdir $false
    + CategoryInfo          : InvalidData: (:) [Invoke-DOSCommands], ParameterBindin...mationException
    + FullyQualifiedErrorId : ParameterArgumentTransformationError,Invoke-DOSCommands

I've searched for this error but can't figure it out. It seems to me that it can't convert the string type correctly! Please help!

回答1:

Your best bet is to use --% in PowerShell V3 or higher. See this blog post I wrote on using --%. In V1/V2 the situation is just bad as you can see in this Connect bug on the issue. The common workaround in V1/V2 is to use Start-Process or .NET's Process.Start. From those list of workarounds, I kind of like this one:

[System.Diagnostics.Process]::Start("Cmd", "/c anything you want to put here with embedded quotes, and variables")

Which is effectively what --% does to the parsing of all parameters following it i.e. it parses them in a dumbed down mode similar to cmd.exe parameter parsing including expanding env vars referenced with %envVarName%.



回答2:

Alright based on the answer by Keith I've modified my function as follows:

Function Invoke-DOSCommands {
    Param(
        [Parameter(Position=0,Mandatory=$true)]
        [String]$cmd)

    $comspec =  $env:comspec # have to get comspec
    $cwd = Get-Location # ensure correct working directory
    $pstartinfo = new-object -type System.Diagnostics.processStartInfo -Argumentlist "$comspec","/c $cmd"
    $pstartinfo.WorkingDirectory= $cwd
    Write-Debug "cmd: $pstartinfo.FileName"
    Write-Debug "args: $pstartinfo.Arguments"
    Write-Debug "dir: $pstartinfo.WorkingDirectory"
    #[System.Diagnostics.Process]::Start("Cmd", "/c $cmd")
    [System.Diagnostics.Process]::Start($pstartinfo)
}

Invoke-DOSCommands "Echo ""Hello World"" & dir & pause" # testing function