Search code examples
c#powershellhostingscriptblock

How to incorporate ScriptBlocks into a hosted powershell environment


I've recently started to explore powershell scripting as well as powershell hosting.

From a powershell command prompt the following line executes as I would expect.

"fee" | &{ process { $_; $args } } "fi" "fo" "fum"

This script outputs the "fee" that's in the pipeline ($_) plus the "fi", "fo", and "fum" that are passed in as arguments to the scriptBlock ($args).

I am trying to achieve this same result by using the System.Management.Automation.Powershell class.

When I try this:

        using (var ps = PowerShell.Create())
        {
            var result = ps
                .AddScript(@"&{ process { $_; $args} }")
                    .AddArgument("fi")
                    .AddArgument("fo")
                    .AddArgument("fum")
                .Invoke(Enumerable.Repeat("fee", 1));
        }

my result array contains a single null element (not sure what's going on there).

If I try this:

        using (var ps = PowerShell.Create())
        {
            var result = ps
                .AddCommand("Invoke-Command")
                    .AddParameter("ScriptBlock", ScriptBlock.Create(@"&{ process { $_; $args } }"))
                    .AddParameter("ArgumentList", new string[] { "fi", "fo", "fum"})
                .Invoke(Enumerable.Repeat("fee", 1));
        }

only "fee" is output. The $args array is empty.

Finally, when I try this:

        using (var ps = PowerShell.Create())
        {
            var result = ps
                .AddCommand("Invoke-Command")
                    .AddParameter("ScriptBlock", ScriptBlock.Create(@"&{ process { $_; $args } } ""fi"" ""fo"" ""fum"""))
                .Invoke(Enumerable.Repeat("fee", 1));
        }

I get closest. The pipelined "fee", and the "fi", "fo", and "fum" arguments are all output. The problem with this, however, is that I ideally would prefer to dynamically pass my arguments to the scriptBlock rather than concatenating them into the script block expression.

My question is how can I create a script block in a hosted powershell environment that can both access data in the pipeline from which it is invoked as well as accept parameters? I've only been able to achieve one or the other. I am using version 4.5 of the .Net framework and version 3 of the System.Management.Automation assembly.


Solution

  • It's because a wrong script is used for AddScript, &{} should not be used because it is actually done by Invoke() in the C# code. Here is the PowerShell analogue of C# code that works:

    $ps = [powershell]::Create()
    $ps.AddScript('process { $_; $args}').
    AddArgument('fi').
    AddArgument('fo').
    AddArgument('fum').
    Invoke(@('fee'))
    

    Output:

    fee
    fi
    fo
    fum
    

    as expected.