How to use dash argument in Powershell?

Viewed 3766

I am porting a script from bash to PowerShell, and I would like to keep the same support for argument parsing in both. In the bash, one of the possible arguments is -- and I want to also detect that argument in PowerShell. However, nothing I've tried so far has worked. I cannot define it as an argument like param($-) as that causes a compile error. Also, if I decide to completely forego PowerShell argument processing, and just use $args everything appears good, but when I run the function, the -- argument is missing.

Function Test-Function {
    Write-Host $args
}

Test-Function -- -args go -here # Prints "-args go -here"

I know about $PSBoundParameters as well, but the value isn't there, because I can't bind a parameter named $-. Are there any other mechanisms here that I can try, or any solution?

For a bit more context, note that me using PowerShell is a side effect. This isn't expected to be used as a normal PowerShell command, I have also written a batch wrapper around this, but the logic of the wrapper is more complex than I wanted to write in batch, so the batch wrapper just calls the PowerShell function, which then does the more complex processing.

2 Answers

I found a way to do so, but instead of double-hyphen you have to pass 3 of them.

This is a simple function, you can change the code as you want:

function Test-Hyphen {
    param(
        ${-}
    )
    if (${-}) {
        write-host "You used triple-hyphen"
    } else {
        write-host "You didn't use triple-hyphen"
    }
}

Sample 1

Test-Hyphen

Output

You didn't use triple-hyphen

Sample 2

Test-Hyphen ---

Output

You used triple-hyphen

As an aside: PowerShell allows a surprising range of variable names, but you have to enclose them in {...} in order for them to be recognized; that is, ${-} technically works, but it doesn't solve your problem.

The challenge is that PowerShell quietly strips -- from the list of arguments - and the only way to preserve that token is you precede it with the PSv3+ stop-parsing symbol, --%, which, however, fundamentally changes how the arguments are passed and is obviously an extra requirement, which is what you're trying to avoid.

Your best bet is to try - suboptimal - workarounds:

  • Option A: In your batch-file wrapper, translate -- to a special argument that PowerShell does preserve and pass it instead; the PowerShell script will then have to re-translate that special argument to --.

  • Option B: Perform custom argument parsing in PowerShell:

You can analyze $MyInvocation.Line, which contains the raw command line that invoked your script, and look for the presence of -- there.

Getting this right and making it robust is nontrivial, however.
Here's a reasonably robust approach:

# Don't use `param()` or `$args` - instead, do your own argument parsing:

# Extract the argument list from the invocation command line.
$argList = ($MyInvocation.Line -replace ('^.*' + [regex]::Escape($MyInvocation.InvocationName)) -split '[;|]')[0].Trim()

# Use Invoke-Expression with a Write-Output call to parse the raw argument list,
# performing evaluation and splitting it into an array:
$customArgs = if ($argList) { @(Invoke-Expression "Write-Output -- $argList") } else { @() }

# Print the resulting arguments array for verification:
$i = 0
$customArgs | % { "Arg #$((++$i)): [$_]" }

Note:

  • There are undoubtedly edge cases where the argument list may not be correctly extracted or where the re-evaluation of the raw arguments causes side effect, but for the majority of cases - especially when called from outside PowerShell - this should do.

  • While useful here, Invoke-Expression should generally be avoided.

If your script is named foo.ps1 and you invoked it as ./foo.ps1 -- -args go -here, you'd see the following output:

Arg #1: [--]
Arg #2: [-args]
Arg #3: [go]
Arg #4: [-here]
Related