Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to stream the output of a programmatically executed ScriptBlock?

My program executes user-specified scriptblocks and I want it to return its output incrementally (e.g. in case the scriptblock runs for a long time).

However, the API of ScriptBlock does not seem to expose anything pipeline-related!

It has some functions that look like they're what I need (InvokeWithPipe), but they're internal and their arguments are of internal types. I'd hate to resort to hacking into reflection here.

So, is there a way to access the scriptblock's pipeline? Maybe some kind of robust workaround?

like image 999
jkff Avatar asked Mar 03 '11 16:03

jkff


1 Answers

Here's some code that will add an extension method to ScriptBlock to stream output, calling a delegate for each output object. It is true streaming as the objects are not backed up in a collection. This is for PowerShell 2.0 or later.

public static class ScriptBlockStreamingExtensions {
       public static void ForEachObject<T>(
            this ScriptBlock script,
            Action<T> action,
            IDictionary parameters) {

            using (var ps = PowerShell.Create()) {

                ps.AddScript(script.ToString());

                if (parameters != null) {
                    ps.AddParameters(parameters);
                }

                ps.Invoke(Enumerable.Empty<object>(), // input
                          new ForwardingNullList<T>(action)); // output
            }
        }

        private class ForwardingNullList<T> : IList<T> {
            private readonly Action<T> _elementAction;

            internal ForwardingNullList(Action<T> elementAction) {
                _elementAction = elementAction;
            }

            #region Implementation of IEnumerable
            //  members throw NotImplementedException
            #endregion

            #region Implementation of ICollection<T>
            // other members throw NotImplementedException

            public int Count {
                get {
                    return 0;
                }
            }
            #endregion

            #region Implementation of IList<T>
            // other members throw NotImplementedException

            public void Insert(int index, T item) {
                    _elementAction(item);
            }
            #endregion
       }
}

Example:

// execute a scriptblock with parameters
ScriptBlock script = ScriptBlock.Create("param($x, $y); $x+$y");
script.ForEachObject<int>(Console.WriteLine,
    new Dictionary<string,object> {{"x", 2},{"y", 3}});

(updated 2011/3/7 with parameter support)

Hope this helps.

like image 150
x0n Avatar answered Nov 15 '22 09:11

x0n