public override AstVisitAction VisitPipeline(PipelineAst pipelineAst)
{
// shouldn't happen, but anyway
if (!pipelineAst.PipelineElements.Any())
{
return AstVisitAction.SkipChildren;
}
// Pipeline uses global execution context, so we should set its WriteSideEffects flag, and restore it after.
// TODO: I'm not very sure about that changing context and WriteSideEffectsToPipeline stuff
var pipeLineContext = ExecutionContext.CurrentRunspace.ExecutionContext;
bool writeSideEffects = pipeLineContext.WriteSideEffectsToPipeline;
try
{
pipeLineContext.WriteSideEffectsToPipeline = _writeSideEffectsToPipeline;
var pipeline = ExecutionContext.CurrentRunspace.CreateNestedPipeline();
int startAt = 0; // set to 1 if first element is an expression
int pipelineCommandCount = pipelineAst.PipelineElements.Count;
// first element of pipeline can be an expression that needs to be evaluated
var expression = pipelineAst.PipelineElements[0] as CommandExpressionAst;
if (expression != null)
{
// evaluate it and get results
var subVisitor = this.CloneSub(_writeSideEffectsToPipeline);
expression.Expression.Visit(subVisitor);
var results = subVisitor._pipelineCommandRuntime.OutputStream.Read();
// if we only have that one expression and no commands, write expression to output and return
if (pipelineCommandCount == 1)
{
_pipelineCommandRuntime.WriteObject(results, true);
VisitRedirections(expression);
return AstVisitAction.SkipChildren;
}
// otherwise write value to input of pipeline to be processed
if (results.Count == 1)
{
// "unroll" an input array: write all enumerated elements seperately to the pipeline
pipeline.Input.Write(results[0], true);
}
else
{
pipeline.Input.Write(results, true);
}
startAt = 1;
}
else // if there was no expression we take the input of the context's input stream
{
foreach (var input in ExecutionContext.InputStream.Read())
{
pipeline.Input.Write(input);
}
}
// all other elements *need* to be commands (same in PS). Make that sure and add them to the pipeline
for (int curCommand = startAt; curCommand < pipelineCommandCount; curCommand++)
{
var commandAst = pipelineAst.PipelineElements[curCommand] as CommandAst;
if (commandAst == null)
{
throw new NotSupportedException("Invalid command in pipeline."
+ " Only the first element of a pipeline can be an expression.");
}
var command = GetCommand(commandAst);
commandAst.CommandElements
// the first CommandElements is the command itself. The rest are parameters/arguments
.Skip(1)
.Select(ConvertCommandElementToCommandParameter)
.ForEach(command.Parameters.Add);
command.RedirectionVisitor = new RedirectionVisitor(this, commandAst.Redirections);
pipeline.Commands.Add(command);
}
// now execute the pipeline
ExecutionContext.PushPipeline(pipeline);
// rerouting the output and error stream would be easier, but Pipeline doesn't
// have an interface for this. So let's catch the exception for now, read the streams
// and rethrow it afterwards
Exception exception = null;
Collection<PSObject> pipeResults = null;
try
{
pipeResults = pipeline.Invoke();
}
catch (Exception e)
{
exception = e;
pipeResults = pipeline.Output.NonBlockingRead();
}
// read output and error and write them as results of the current commandRuntime
foreach (var curResult in pipeResults)
{
_pipelineCommandRuntime.WriteObject(curResult);
}
var errors = pipeline.Error.NonBlockingRead();
foreach (var curError in errors)
{
_pipelineCommandRuntime.ErrorStream.Write(curError);
}
ExecutionContext.PopPipeline();
if (exception != null)
{
throw exception;
}
}
finally
{
pipeLineContext.WriteSideEffectsToPipeline = writeSideEffects;
}
return AstVisitAction.SkipChildren;
}