Add pipeline redirection support (#4594)

* redirection

* Remove commented-out

* fix tests

* more fixes
This commit is contained in:
JT
2022-02-21 17:22:21 -05:00
committed by GitHub
parent 739e403cd5
commit 9888f8f298
46 changed files with 463 additions and 288 deletions

View File

@ -133,7 +133,7 @@ fn eval_call(
}
}
let result = eval_block(engine_state, &mut callee_stack, block, input);
let result = eval_block(engine_state, &mut callee_stack, block, input, false, true);
if block.redirect_env {
let caller_env_vars = caller_stack.get_env_var_names(engine_state);
@ -169,7 +169,8 @@ fn eval_external(
head: &Expression,
args: &[Expression],
input: PipelineData,
last_expression: bool,
redirect_stdout: bool,
redirect_stderr: bool,
) -> Result<PipelineData, ShellError> {
let decl_id = engine_state
.find_decl("run-external".as_bytes())
@ -185,10 +186,20 @@ fn eval_external(
call.positional.push(arg.clone())
}
if last_expression {
if redirect_stdout {
call.named.push((
Spanned {
item: "last-expression".into(),
item: "redirect-stdout".into(),
span: head.span,
},
None,
))
}
if redirect_stderr {
call.named.push((
Spanned {
item: "redirect-stderr".into(),
span: head.span,
},
None,
@ -277,6 +288,7 @@ pub fn eval_expression(
args,
PipelineData::new(span),
false,
false,
)?
.into_value(span))
}
@ -431,20 +443,37 @@ pub fn eval_expression_with_input(
stack: &mut Stack,
expr: &Expression,
mut input: PipelineData,
last_expression: bool,
redirect_stdout: bool,
redirect_stderr: bool,
) -> Result<PipelineData, ShellError> {
match expr {
Expression {
expr: Expr::Call(call),
..
} => {
input = eval_call(engine_state, stack, call, input)?;
if !redirect_stdout || redirect_stderr {
// we're doing something different than the defaults
let mut call = call.clone();
call.redirect_stdout = redirect_stdout;
call.redirect_stderr = redirect_stderr;
input = eval_call(engine_state, stack, &call, input)?;
} else {
input = eval_call(engine_state, stack, call, input)?;
}
}
Expression {
expr: Expr::ExternalCall(head, args),
..
} => {
input = eval_external(engine_state, stack, head, args, input, last_expression)?;
input = eval_external(
engine_state,
stack,
head,
args,
input,
redirect_stdout,
redirect_stderr,
)?;
}
Expression {
@ -470,6 +499,8 @@ pub fn eval_block(
stack: &mut Stack,
block: &Block,
mut input: PipelineData,
redirect_stdout: bool,
redirect_stderr: bool,
) -> Result<PipelineData, ShellError> {
let num_pipelines = block.len();
for (pipeline_idx, pipeline) in block.pipelines.iter().enumerate() {
@ -479,7 +510,8 @@ pub fn eval_block(
stack,
elem,
input,
i == pipeline.expressions.len() - 1,
redirect_stdout || (i != pipeline.expressions.len() - 1),
redirect_stderr,
)?
}
@ -543,78 +575,6 @@ pub fn eval_block(
Ok(input)
}
pub fn eval_block_with_redirect(
engine_state: &EngineState,
stack: &mut Stack,
block: &Block,
mut input: PipelineData,
) -> Result<PipelineData, ShellError> {
let num_pipelines = block.len();
for (pipeline_idx, pipeline) in block.pipelines.iter().enumerate() {
for elem in pipeline.expressions.iter() {
input = eval_expression_with_input(engine_state, stack, elem, input, false)?
}
if pipeline_idx < (num_pipelines) - 1 {
match input {
PipelineData::Value(Value::Nothing { .. }, ..) => {}
_ => {
// Drain the input to the screen via tabular output
let config = stack.get_config().unwrap_or_default();
match engine_state.find_decl("table".as_bytes()) {
Some(decl_id) => {
let table = engine_state.get_decl(decl_id).run(
engine_state,
stack,
&Call::new(Span::new(0, 0)),
input,
)?;
for item in table {
let stdout = std::io::stdout();
if let Value::Error { error } = item {
return Err(error);
}
let mut out = item.into_string("\n", &config);
out.push('\n');
match stdout.lock().write_all(out.as_bytes()) {
Ok(_) => (),
Err(err) => eprintln!("{}", err),
};
}
}
None => {
for item in input {
let stdout = std::io::stdout();
if let Value::Error { error } = item {
return Err(error);
}
let mut out = item.into_string("\n", &config);
out.push('\n');
match stdout.lock().write_all(out.as_bytes()) {
Ok(_) => (),
Err(err) => eprintln!("{}", err),
};
}
}
};
}
}
input = PipelineData::new(Span { start: 0, end: 0 })
}
}
Ok(input)
}
pub fn eval_subexpression(
engine_state: &EngineState,
stack: &mut Stack,
@ -623,7 +583,7 @@ pub fn eval_subexpression(
) -> Result<PipelineData, ShellError> {
for pipeline in block.pipelines.iter() {
for expr in pipeline.expressions.iter() {
input = eval_expression_with_input(engine_state, stack, expr, input, false)?
input = eval_expression_with_input(engine_state, stack, expr, input, true, false)?
}
}