views:

2146

answers:

3

I have two PowerShell functions, the first of which invokes the second. They both take N arguments, and one of them is defined to simply add a flag and invoke the other. Here are example definitions:

function inner
{
  foreach( $arg in $args )
    {
      # do some stuff
    }
}

function outer
{
  inner --flag $args
}

Usage would look something like this:

inner foo bar baz

or this

outer wibble wobble wubble

The goal is for the latter example to be equivalent to

inner --flag wibble wobble wubble

The Problem: As defined here, the latter actually results in two arguments being passed to inner: the first is "--flag", and the second is an array containing "wibble", "wobble", and "wubble". What I want is for inner to receive four arguments: the flag and the three original arguments.

So what I'm wondering is how to convince powershell to expand the $args array before passing it to inner, passing it as N elements rather than a single array. I believe you can do this in Ruby with the splatting operator (the * character), and I'm pretty sure PowerShell can do it, but I don't recall how.

A: 

Well, there may be a better way, but see if this works:

inner --flag [string]::Join(" ", $args)
EBGreen
It looks like this doesn't work directly, because the result of string::Join is passed as a single argument, in this case "wibble wobble wubble".
Charlie
hmmm...let me poke around some more
EBGreen
A: 

Building on @EBGreen's idea, and a related question I noticed in the sidebar, a possible solution is this:

function outer
{
    invoke-expression "inner --flag $($args -join ' ')"
}

Note: This example makes use of the Powershell 2.0 CTP's new -join operator.

However, I'd still like to find a better method, since this feels like a hack and is horrible security-wise.

Charlie
I'm not a fan of invoke expression either, but if you control of the args it isn't terrible. I think CTP2 or CTP3 exposes the tokenizer too in which case you could sanitize the args before the invoke.
EBGreen
You can do this with PowerShell v1.0 only stuff as well:invoke-expression "inner --flag $args"The array will be joined with spaces by default (there is also a way to change the default character, but I can't remember it).
JasonMArcher
Yup, you're correct about that. The separator is controlled by the $OFS variable.
Charlie
+2  A: 

There isn't a good solution to this problem in PowerSHell V1. In V2 we added splatting (though for various reasons, we use @ instead of * for this purpose). Here's what it looks like:

PS (STA-ISS) (2) > function foo ($x,$y,$z) { "x:$x y:$y z:$z" }

PS (STA-ISS) (3) > $a = 1,2,3

PS (STA-ISS) (4) > foo $a # passed as single arg

x:1 2 3 y: z:

PS (STA-ISS) (5) > foo @a # splatted

x:1 y:2 z:3

Bruce Payette
What an honor to get an answer from one of the PowerShell gurus! I have PowerShell 2 CTP2 installed, so I'll give that a try.
Charlie
Looks like that's exactly what I needed, thanks again.
Charlie