Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing an associative array from c# to Powershell

I'd like to pass an associative array from C# to Powershell. As an example I'd like to execute this powershell line of code:


PS C:\> get-command | select name, @{N="Foo";E={"Bar"}} -first 3

Name                                                        Foo
----                                                        ---
Add-Content                                                 Bar
Add-History                                                 Bar
Add-Member                                                  Bar

I'd like to do this via a Pipeline of distinct Commands as opposed to a single command marked as a script. Here's the code:


Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();

pipeline.Commands.Add("get-command");

Command c = new Command("select-object");
List properties = new List();
properties.Add("name");
properties.Add("@{N=\"Foo\";E={\"Bar\"}}");
c.Parameters.Add("Property", properties.ToArray());
c.Parameters.Add("First", 3);
pipeline.Commands.Add(c);

pipeline.Commands.Add("Out-String");

Collection retval = pipeline.Invoke();
runspace.Close();

StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in retval)
    Console.WriteLine(obj.ToString());

But that associative array being passed in as a parameter to Select-Object isn't being parsed correctly. This is what comes out the other side:


PS C:\test> c:\test\Bin\Debug\test.exe

Name                                     @{N="Foo";E={"Bar"}}
----                                     --------------------
Add-Content
Add-History
Add-Member

What's wrong with how I'm setting up the Select-Object command parameters?

like image 377
xcud Avatar asked Mar 06 '09 17:03

xcud


People also ask

How do you pass an associative array to a function?

You can only pass associative arrays by name. It's better (more efficient) to pass regular arrays by name also. You would do something like eval echo "\${$1[$key]}" in the function, and pass in the name of the variable, without the $ .

What is an associative array in C?

Associative arrays are used to represent collections of data elements that can be retrieved by specifying a name called a key. D associative array keys are formed by a list of scalar expression values called a tuple.

How do you access an associative array?

The elements of an associative array can only be accessed by the corresponding keys. As there is not strict indexing between the keys, accessing the elements normally by integer index is not possible in PHP. Although the array_keys() function can be used to get an indexed array of keys for an associative array.

What is an associative array give an example?

Associative arrays are used to store key value pairs. For example, to store the marks of different subject of a student in an array, a numerically indexed array would not be the best choice.


1 Answers

Creating a pipeline through c# and creating a pipeline with native powershell script have one major difference that is actually quite subtle: the parameter binder.

if I write a version of your code in pure script, I will get the same error: the hashtable literal is treated as a string value.

ps> $ps = $ps.Commands.Add("get-process")
ps> $ps = $ps.Commands.Add("select-object")
ps> $ps.Commands[1].Parameters.Add("Property", @("Name", '@{N="Foo";E={"Bar"}}'))

In this case, the command receives an array of two strings, the "name" and the hashtable literal string. This will be broken in exactly the same way as your C#. Now, take a look at the right way to do it (in script) - let me rewrite line 3:

ps> $ps.Commands[1].Parameters.Add("Property", @("Name", @{N="Foo";E={"Bar"}}))

So what changed? I removed the quotes around the hashtable -- I am passing a hashtable as the 2nd element of the object array! So, to get your C# example to work, you need to do what the parameter binder does for us at the command line (which is quite a lot!). Replace:

properties.Add("@{N=\"Foo\";E={\"Bar\"}}");

with

properties.Add(
    new Hashtable {
        {"N", "Foo"},
        {"E", System.Mananagement.Automation.ScriptBlock.Create("\"Foo\"")}
    }
);

I hope this clears it up for you. The parameter binder is probably the least visible but most powerful part of the powershell experience.

-Oisin

like image 123
x0n Avatar answered Sep 22 '22 02:09

x0n