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

@ -84,7 +84,14 @@ impl Command for Do {
)
}
}
let result = eval_block(engine_state, &mut stack, block, input);
let result = eval_block(
engine_state,
&mut stack,
block,
input,
call.redirect_stdout,
ignore_errors,
);
if ignore_errors {
match result {

View File

@ -1,4 +1,4 @@
use nu_engine::{eval_block_with_redirect, eval_expression, CallExt};
use nu_engine::{eval_block, eval_expression, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack};
use nu_protocol::{
@ -71,6 +71,8 @@ impl Command for For {
let mut stack = stack.captures_to_stack(&capture_block.captures);
let orig_env_vars = stack.env_vars.clone();
let orig_env_hidden = stack.env_hidden.clone();
let redirect_stdout = call.redirect_stdout;
let redirect_stderr = call.redirect_stderr;
match values {
Value::List { vals, .. } => Ok(vals
@ -99,11 +101,13 @@ impl Command for For {
);
//let block = engine_state.get_block(block_id);
match eval_block_with_redirect(
match eval_block(
&engine_state,
&mut stack,
&block,
PipelineData::new(head),
redirect_stdout,
redirect_stderr,
) {
Ok(pipeline_data) => pipeline_data.into_value(head),
Err(error) => Value::Error { error },
@ -137,11 +141,13 @@ impl Command for For {
);
//let block = engine_state.get_block(block_id);
match eval_block_with_redirect(
match eval_block(
&engine_state,
&mut stack,
&block,
PipelineData::new(head),
redirect_stdout,
redirect_stderr,
) {
Ok(pipeline_data) => pipeline_data.into_value(head),
Err(error) => Value::Error { error },
@ -152,7 +158,14 @@ impl Command for For {
x => {
stack.add_var(var_id, x);
eval_block_with_redirect(&engine_state, &mut stack, &block, PipelineData::new(head))
eval_block(
&engine_state,
&mut stack,
&block,
PipelineData::new(head),
redirect_stdout,
redirect_stderr,
)
}
}
}

View File

@ -51,7 +51,14 @@ impl Command for If {
if *val {
let block = engine_state.get_block(then_block.block_id);
let mut stack = stack.captures_to_stack(&then_block.captures);
eval_block(engine_state, &mut stack, block, input)
eval_block(
engine_state,
&mut stack,
block,
input,
call.redirect_stdout,
call.redirect_stderr,
)
} else if let Some(else_case) = else_case {
if let Some(else_expr) = else_case.as_keyword() {
if let Some(block_id) = else_expr.as_block() {
@ -60,7 +67,14 @@ impl Command for If {
let mut stack = stack.captures_to_stack(&else_block.captures);
let block = engine_state.get_block(block_id);
eval_block(engine_state, &mut stack, block, input)
eval_block(
engine_state,
&mut stack,
block,
input,
call.redirect_stdout,
call.redirect_stderr,
)
} else {
eval_expression(engine_state, stack, else_expr)
.map(|x| x.into_pipeline_data())

View File

@ -41,7 +41,14 @@ impl Command for Let {
.as_keyword()
.expect("internal error: missing keyword");
let rhs = eval_expression_with_input(engine_state, stack, keyword_expr, input, false)?;
let rhs = eval_expression_with_input(
engine_state,
stack,
keyword_expr,
input,
call.redirect_stdout,
call.redirect_stderr,
)?;
//println!("Adding: {:?} to {}", rhs, var_id);

View File

@ -38,7 +38,14 @@ impl Command for Source {
let block_id: i64 = call.req(engine_state, stack, 1)?;
let block = engine_state.get_block(block_id as usize).clone();
eval_block(engine_state, stack, &block, input)
eval_block(
engine_state,
stack,
&block,
input,
call.redirect_stdout,
call.redirect_stderr,
)
}
fn examples(&self) -> Vec<Example> {

View File

@ -96,8 +96,15 @@ impl Command for Use {
// TODO: Add string conversions (e.g. int to string)
// TODO: Later expand env to take all Values
let val = eval_block(engine_state, stack, block, PipelineData::new(call.head))?
.into_value(call.head);
let val = eval_block(
engine_state,
stack,
block,
PipelineData::new(call.head),
false,
true,
)?
.into_value(call.head);
stack.add_env_var(name, val);
}

View File

@ -72,6 +72,8 @@ pub fn test_dataframe(cmds: Vec<Box<dyn Command + 'static>>) {
&mut stack,
&block,
PipelineData::new(Span::test_data()),
true,
true,
) {
Err(err) => panic!("test eval error in `{}`: {:?}", example.example, err),
Ok(result) => {

View File

@ -39,8 +39,9 @@ impl Command for LetEnv {
.as_keyword()
.expect("internal error: missing keyword");
let rhs = eval_expression_with_input(engine_state, stack, keyword_expr, input, false)?
.into_value(call.head);
let rhs =
eval_expression_with_input(engine_state, stack, keyword_expr, input, false, true)?
.into_value(call.head);
if env_var == "PWD" {
let cwd = current_dir(engine_state, stack)?;

View File

@ -132,7 +132,14 @@ fn with_env(
stack.add_env_var(k, v);
}
eval_block(engine_state, &mut stack, block, input)
eval_block(
engine_state,
&mut stack,
block,
input,
call.redirect_stdout,
call.redirect_stderr,
)
}
#[cfg(test)]

View File

@ -121,6 +121,8 @@ pub fn test_examples(cmd: impl Command + 'static) {
&mut stack,
&block,
PipelineData::new(Span::test_data()),
true,
true,
) {
Err(err) => panic!("test eval error in `{}`: {:?}", example.example, err),
Ok(result) => {

View File

@ -68,10 +68,17 @@ impl Command for All {
stack.add_var(var_id, value);
}
eval_block(&engine_state, &mut stack, block, PipelineData::new(span))
.map_or(false, |pipeline_data| {
pipeline_data.into_value(span).is_true()
})
eval_block(
&engine_state,
&mut stack,
block,
PipelineData::new(span),
call.redirect_stdout,
call.redirect_stderr,
)
.map_or(false, |pipeline_data| {
pipeline_data.into_value(span).is_true()
})
}),
span,
}

View File

@ -67,10 +67,17 @@ impl Command for Any {
stack.add_var(var_id, value);
}
eval_block(&engine_state, &mut stack, block, PipelineData::new(span))
.map_or(false, |pipeline_data| {
pipeline_data.into_value(span).is_true()
})
eval_block(
&engine_state,
&mut stack,
block,
PipelineData::new(span),
call.redirect_stdout,
call.redirect_stderr,
)
.map_or(false, |pipeline_data| {
pipeline_data.into_value(span).is_true()
})
}),
span,
}

View File

@ -50,6 +50,8 @@ impl Command for Collect {
&mut stack,
&block,
PipelineData::new(call.head),
call.redirect_stdout,
call.redirect_stderr,
)
}

View File

@ -1,4 +1,4 @@
use nu_engine::{eval_block_with_redirect, CallExt};
use nu_engine::{eval_block, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack};
use nu_protocol::{
@ -110,6 +110,8 @@ impl Command for Each {
let orig_env_vars = stack.env_vars.clone();
let orig_env_hidden = stack.env_hidden.clone();
let span = call.head;
let redirect_stdout = call.redirect_stdout;
let redirect_stderr = call.redirect_stderr;
match input {
PipelineData::Value(Value::Range { .. }, ..)
@ -143,11 +145,13 @@ impl Command for Each {
}
}
match eval_block_with_redirect(
match eval_block(
&engine_state,
&mut stack,
&block,
PipelineData::new(span),
redirect_stdout,
redirect_stderr,
) {
Ok(v) => v.into_value(span),
Err(error) => Value::Error { error },
@ -188,87 +192,34 @@ impl Command for Each {
}
}
match eval_block_with_redirect(
match eval_block(
&engine_state,
&mut stack,
&block,
PipelineData::new(span),
redirect_stdout,
redirect_stderr,
) {
Ok(v) => v.into_value(span),
Err(error) => Value::Error { error },
}
})
.into_pipeline_data(ctrlc)),
// JT: we'll turn this off for now until we get a better design
// leaving it here, but commented-out, for the time being
// PipelineData::Value(Value::Record { cols, vals, .. }, ..) => {
// let mut output_cols = vec![];
// let mut output_vals = vec![];
// for (col, val) in cols.into_iter().zip(vals.into_iter()) {
// //let block = engine_state.get_block(block_id);
// stack.with_env(&orig_env_vars, &orig_env_hidden);
// if let Some(var) = block.signature.get_positional(0) {
// if let Some(var_id) = &var.var_id {
// stack.add_var(
// *var_id,
// Value::Record {
// cols: vec!["column".into(), "value".into()],
// vals: vec![
// Value::String {
// val: col.clone(),
// span: call.head,
// },
// val,
// ],
// span: call.head,
// },
// );
// }
// }
// match eval_block_with_redirect(
// &engine_state,
// &mut stack,
// &block,
// PipelineData::new(span),
// )? {
// PipelineData::Value(
// Value::Record {
// mut cols, mut vals, ..
// },
// ..,
// ) => {
// // TODO check that the lengths match when traversing record
// output_cols.append(&mut cols);
// output_vals.append(&mut vals);
// }
// x => {
// output_cols.push(col);
// output_vals.push(x.into_value(span));
// }
// }
// }
// Ok(Value::Record {
// cols: output_cols,
// vals: output_vals,
// span: call.head,
// }
// .into_pipeline_data())
// }
PipelineData::Value(x, ..) => {
//let block = engine_state.get_block(block_id);
if let Some(var) = block.signature.get_positional(0) {
if let Some(var_id) = &var.var_id {
stack.add_var(*var_id, x);
}
}
eval_block_with_redirect(&engine_state, &mut stack, &block, PipelineData::new(span))
eval_block(
&engine_state,
&mut stack,
&block,
PipelineData::new(span),
redirect_stdout,
redirect_stderr,
)
}
}
.and_then(|x| {

View File

@ -1,4 +1,4 @@
use nu_engine::{eval_block_with_redirect, CallExt};
use nu_engine::{eval_block, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack};
use nu_protocol::{
@ -69,6 +69,8 @@ impl Command for EachGroup {
engine_state: engine_state.clone(),
stack: stack.clone(),
group_size: group_size.item,
redirect_stdout: call.redirect_stdout,
redirect_stderr: call.redirect_stderr,
input: Box::new(input.into_iter()),
span: call.head,
};
@ -82,6 +84,8 @@ struct EachGroupIterator {
engine_state: EngineState,
stack: Stack,
group_size: usize,
redirect_stdout: bool,
redirect_stderr: bool,
input: Box<dyn Iterator<Item = Value> + Send>,
span: Span,
}
@ -118,6 +122,8 @@ impl Iterator for EachGroupIterator {
self.block.clone(),
self.engine_state.clone(),
self.stack.clone(),
self.redirect_stdout,
self.redirect_stderr,
self.span,
))
}
@ -128,6 +134,8 @@ pub(crate) fn run_block_on_vec(
capture_block: CaptureBlock,
engine_state: EngineState,
stack: Stack,
redirect_stdout: bool,
redirect_stderr: bool,
span: Span,
) -> Value {
let value = Value::List { vals: input, span };
@ -142,7 +150,14 @@ pub(crate) fn run_block_on_vec(
}
}
match eval_block_with_redirect(&engine_state, &mut stack, block, PipelineData::new(span)) {
match eval_block(
&engine_state,
&mut stack,
block,
PipelineData::new(span),
redirect_stdout,
redirect_stderr,
) {
Ok(pipeline) => pipeline.into_value(span),
Err(error) => Value::Error { error },
}

View File

@ -1,4 +1,4 @@
use nu_engine::{eval_block_with_redirect, CallExt};
use nu_engine::{eval_block, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack};
use nu_protocol::{
@ -108,6 +108,8 @@ impl Command for EachWindow {
stack: stack.clone(),
group_size: group_size.item,
input: Box::new(input.into_iter()),
redirect_stdout: call.redirect_stdout,
redirect_stderr: call.redirect_stderr,
span: call.head,
previous: vec![],
stride,
@ -123,6 +125,8 @@ struct EachWindowIterator {
stack: Stack,
group_size: usize,
input: Box<dyn Iterator<Item = Value> + Send>,
redirect_stdout: bool,
redirect_stderr: bool,
span: Span,
previous: Vec<Value>,
stride: usize,
@ -186,6 +190,8 @@ impl Iterator for EachWindowIterator {
self.block.clone(),
self.engine_state.clone(),
self.stack.clone(),
self.redirect_stdout,
self.redirect_stderr,
self.span,
))
}
@ -196,6 +202,8 @@ pub(crate) fn run_block_on_vec(
capture_block: CaptureBlock,
engine_state: EngineState,
stack: Stack,
redirect_stdout: bool,
redirect_stderr: bool,
span: Span,
) -> Value {
let value = Value::List { vals: input, span };
@ -210,7 +218,14 @@ pub(crate) fn run_block_on_vec(
}
}
match eval_block_with_redirect(&engine_state, &mut stack, block, PipelineData::new(span)) {
match eval_block(
&engine_state,
&mut stack,
block,
PipelineData::new(span),
redirect_stdout,
redirect_stderr,
) {
Ok(pipeline) => pipeline.into_value(span),
Err(error) => Value::Error { error },
}

View File

@ -118,7 +118,14 @@ fn empty(
.ok_or_else(|| ShellError::TypeMismatch("expected row condition".to_owned(), head))?;
let b = engine_state.get_block(block_id);
let evaluated_block = eval_block(engine_state, stack, b, PipelineData::new(head))?;
let evaluated_block = eval_block(
engine_state,
stack,
b,
PipelineData::new(head),
call.redirect_stdout,
call.redirect_stderr,
)?;
Some(evaluated_block.into_value(head))
} else {
None

View File

@ -93,6 +93,9 @@ impl Command for Find {
let metadata = input.metadata();
let config = stack.get_config()?;
let redirect_stdout = call.redirect_stdout;
let redirect_stderr = call.redirect_stderr;
match call.get_flag::<CaptureBlock>(&engine_state, stack, "predicate")? {
Some(predicate) => {
let capture_block = predicate;
@ -121,6 +124,8 @@ impl Command for Find {
&mut stack,
&block,
PipelineData::new_with_metadata(metadata.clone(), span),
redirect_stdout,
redirect_stderr,
)
.map_or(false, |pipeline_data| {
pipeline_data.into_value(span).is_true()

View File

@ -119,8 +119,14 @@ pub fn group_by(
if let Some(capture_block) = &block {
let mut stack = stack.captures_to_stack(&capture_block.captures);
let block = engine_state.get_block(capture_block.block_id);
let pipeline =
eval_block(engine_state, &mut stack, block, value.into_pipeline_data());
let pipeline = eval_block(
engine_state,
&mut stack,
block,
value.into_pipeline_data(),
call.redirect_stdout,
call.redirect_stderr,
);
match pipeline {
Ok(s) => {

View File

@ -58,6 +58,9 @@ impl Command for KeepUntil {
let ctrlc = engine_state.ctrlc.clone();
let engine_state = engine_state.clone();
let redirect_stdout = call.redirect_stdout;
let redirect_stderr = call.redirect_stderr;
Ok(input
.into_iter()
.take_while(move |value| {
@ -65,10 +68,17 @@ impl Command for KeepUntil {
stack.add_var(var_id, value.clone());
}
!eval_block(&engine_state, &mut stack, &block, PipelineData::new(span))
.map_or(false, |pipeline_data| {
pipeline_data.into_value(span).is_true()
})
!eval_block(
&engine_state,
&mut stack,
&block,
PipelineData::new(span),
redirect_stdout,
redirect_stderr,
)
.map_or(false, |pipeline_data| {
pipeline_data.into_value(span).is_true()
})
})
.into_pipeline_data(ctrlc))
}

View File

@ -58,6 +58,9 @@ impl Command for KeepWhile {
let ctrlc = engine_state.ctrlc.clone();
let engine_state = engine_state.clone();
let redirect_stdout = call.redirect_stdout;
let redirect_stderr = call.redirect_stderr;
Ok(input
.into_iter()
.take_while(move |value| {
@ -65,10 +68,17 @@ impl Command for KeepWhile {
stack.add_var(var_id, value.clone());
}
eval_block(&engine_state, &mut stack, &block, PipelineData::new(span))
.map_or(false, |pipeline_data| {
pipeline_data.into_value(span).is_true()
})
eval_block(
&engine_state,
&mut stack,
&block,
PipelineData::new(span),
redirect_stdout,
redirect_stderr,
)
.map_or(false, |pipeline_data| {
pipeline_data.into_value(span).is_true()
})
})
.into_pipeline_data(ctrlc))
}

View File

@ -82,6 +82,8 @@ impl Command for Merge {
&mut stack,
block,
PipelineData::new(call.head),
call.redirect_stdout,
call.redirect_stderr,
);
let table = match result {

View File

@ -1,4 +1,4 @@
use nu_engine::{eval_block_with_redirect, CallExt};
use nu_engine::{eval_block, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack};
use nu_protocol::{
@ -53,6 +53,8 @@ impl Command for ParEach {
let block_id = capture_block.block_id;
let mut stack = stack.captures_to_stack(&capture_block.captures);
let span = call.head;
let redirect_stdout = call.redirect_stdout;
let redirect_stderr = call.redirect_stderr;
match input {
PipelineData::Value(Value::Range { val, .. }, ..) => Ok(val
@ -87,11 +89,13 @@ impl Command for ParEach {
}
}
match eval_block_with_redirect(
match eval_block(
&engine_state,
&mut stack,
block,
PipelineData::new(span),
redirect_stdout,
redirect_stderr,
) {
Ok(v) => v,
Err(error) => Value::Error { error }.into_pipeline_data(),
@ -133,11 +137,13 @@ impl Command for ParEach {
}
}
match eval_block_with_redirect(
match eval_block(
&engine_state,
&mut stack,
block,
PipelineData::new(span),
redirect_stdout,
redirect_stderr,
) {
Ok(v) => v,
Err(error) => Value::Error { error }.into_pipeline_data(),
@ -178,11 +184,13 @@ impl Command for ParEach {
}
}
match eval_block_with_redirect(
match eval_block(
&engine_state,
&mut stack,
block,
PipelineData::new(span),
redirect_stdout,
redirect_stderr,
) {
Ok(v) => v,
Err(error) => Value::Error { error }.into_pipeline_data(),
@ -228,11 +236,13 @@ impl Command for ParEach {
}
}
match eval_block_with_redirect(
match eval_block(
&engine_state,
&mut stack,
block,
PipelineData::new(span),
redirect_stdout,
redirect_stderr,
) {
Ok(v) => v,
Err(error) => Value::Error { error }.into_pipeline_data(),
@ -242,64 +252,6 @@ impl Command for ParEach {
.into_iter()
.flatten()
.into_pipeline_data(ctrlc)),
PipelineData::Value(Value::Record { cols, vals, .. }, ..) => {
let mut output_cols = vec![];
let mut output_vals = vec![];
for (col, val) in cols.into_iter().zip(vals.into_iter()) {
let block = engine_state.get_block(block_id);
let mut stack = stack.clone();
if let Some(var) = block.signature.get_positional(0) {
if let Some(var_id) = &var.var_id {
stack.add_var(
*var_id,
Value::Record {
cols: vec!["column".into(), "value".into()],
vals: vec![
Value::String {
val: col.clone(),
span: call.head,
},
val,
],
span: call.head,
},
);
}
}
match eval_block_with_redirect(
&engine_state,
&mut stack,
block,
PipelineData::new(span),
)? {
PipelineData::Value(
Value::Record {
mut cols, mut vals, ..
},
..,
) => {
// TODO check that the lengths match when traversing record
output_cols.append(&mut cols);
output_vals.append(&mut vals);
}
x => {
output_cols.push(col);
output_vals.push(x.into_value(span));
}
}
}
Ok(Value::Record {
cols: output_cols,
vals: output_vals,
span: call.head,
}
.into_pipeline_data())
}
PipelineData::Value(x, ..) => {
let block = engine_state.get_block(block_id);
@ -309,7 +261,14 @@ impl Command for ParEach {
}
}
eval_block_with_redirect(&engine_state, &mut stack, block, PipelineData::new(span))
eval_block(
&engine_state,
&mut stack,
block,
PipelineData::new(span),
redirect_stdout,
redirect_stderr,
)
}
}
}

View File

@ -1,4 +1,4 @@
use nu_engine::{eval_block_with_redirect, CallExt};
use nu_engine::{eval_block, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack};
use nu_protocol::{
@ -49,6 +49,8 @@ impl Command for ParEachGroup {
let capture_block: CaptureBlock = call.req(engine_state, stack, 1)?;
let ctrlc = engine_state.ctrlc.clone();
let span = call.head;
let redirect_stdout = call.redirect_stdout;
let redirect_stderr = call.redirect_stderr;
let stack = stack.captures_to_stack(&capture_block.captures);
@ -72,11 +74,13 @@ impl Command for ParEachGroup {
}
}
match eval_block_with_redirect(
match eval_block(
engine_state,
&mut stack,
block,
PipelineData::new(span),
redirect_stdout,
redirect_stderr,
) {
Ok(v) => v.into_value(span),
Err(error) => Value::Error { error },

View File

@ -109,6 +109,9 @@ impl Command for Reduce {
let orig_env_vars = stack.env_vars.clone();
let orig_env_hidden = stack.env_hidden.clone();
let redirect_stdout = call.redirect_stdout;
let redirect_stderr = call.redirect_stderr;
let mut input_iter = input.into_iter();
let (off, start_val) = if let Some(val) = fold {
@ -170,7 +173,14 @@ impl Command for Reduce {
}
}
let v = match eval_block(engine_state, &mut stack, block, PipelineData::new(span)) {
let v = match eval_block(
engine_state,
&mut stack,
block,
PipelineData::new(span),
redirect_stdout,
redirect_stderr,
) {
Ok(v) => v.into_value(span),
Err(error) => Value::Error { error },
};

View File

@ -58,6 +58,9 @@ impl Command for SkipUntil {
let ctrlc = engine_state.ctrlc.clone();
let engine_state = engine_state.clone();
let redirect_stdout = call.redirect_stdout;
let redirect_stderr = call.redirect_stderr;
Ok(input
.into_iter()
.skip_while(move |value| {
@ -65,10 +68,17 @@ impl Command for SkipUntil {
stack.add_var(var_id, value.clone());
}
!eval_block(&engine_state, &mut stack, &block, PipelineData::new(span))
.map_or(false, |pipeline_data| {
pipeline_data.into_value(span).is_true()
})
!eval_block(
&engine_state,
&mut stack,
&block,
PipelineData::new(span),
redirect_stdout,
redirect_stderr,
)
.map_or(false, |pipeline_data| {
pipeline_data.into_value(span).is_true()
})
})
.into_pipeline_data(ctrlc)
.set_metadata(metadata))

View File

@ -58,6 +58,9 @@ impl Command for SkipWhile {
let ctrlc = engine_state.ctrlc.clone();
let engine_state = engine_state.clone();
let redirect_stdout = call.redirect_stdout;
let redirect_stderr = call.redirect_stderr;
Ok(input
.into_iter()
.skip_while(move |value| {
@ -65,10 +68,17 @@ impl Command for SkipWhile {
stack.add_var(var_id, value.clone());
}
eval_block(&engine_state, &mut stack, &block, PipelineData::new(span))
.map_or(false, |pipeline_data| {
pipeline_data.into_value(span).is_true()
})
eval_block(
&engine_state,
&mut stack,
&block,
PipelineData::new(span),
redirect_stdout,
redirect_stderr,
)
.map_or(false, |pipeline_data| {
pipeline_data.into_value(span).is_true()
})
})
.into_pipeline_data(ctrlc)
.set_metadata(metadata))

View File

@ -70,6 +70,10 @@ fn update(
let cell_path: CellPath = call.req(engine_state, stack, 0)?;
let replacement: Value = call.req(engine_state, stack, 1)?;
let redirect_stdout = call.redirect_stdout;
let redirect_stderr = call.redirect_stderr;
let engine_state = engine_state.clone();
let ctrlc = engine_state.ctrlc.clone();
@ -97,6 +101,8 @@ fn update(
&mut stack,
&block,
input.clone().into_pipeline_data(),
redirect_stdout,
redirect_stderr,
);
match output {

View File

@ -132,6 +132,9 @@ impl Command for UpdateCells {
let ctrlc = engine_state.ctrlc.clone();
let block: Block = engine_state.get_block(block.block_id).clone();
let redirect_stdout = call.redirect_stdout;
let redirect_stderr = call.redirect_stderr;
let span = call.head;
stack.with_env(&orig_env_vars, &orig_env_hidden);
@ -156,6 +159,8 @@ impl Command for UpdateCells {
stack,
block,
columns,
redirect_stdout,
redirect_stderr,
span,
}
.into_pipeline_data(ctrlc))
@ -168,6 +173,8 @@ struct UpdateCellIterator {
engine_state: EngineState,
stack: Stack,
block: Block,
redirect_stdout: bool,
redirect_stderr: bool,
span: Span,
}
@ -195,6 +202,8 @@ impl Iterator for UpdateCellIterator {
&self.engine_state,
&mut self.stack,
&self.block,
self.redirect_stdout,
self.redirect_stderr,
span,
),
})
@ -207,6 +216,8 @@ impl Iterator for UpdateCellIterator {
&self.engine_state,
&mut self.stack,
&self.block,
self.redirect_stdout,
self.redirect_stderr,
self.span,
)),
}
@ -221,6 +232,8 @@ fn process_cell(
engine_state: &EngineState,
stack: &mut Stack,
block: &Block,
redirect_stdout: bool,
redirect_stderr: bool,
span: Span,
) -> Value {
if let Some(var) = block.signature.get_positional(0) {
@ -228,7 +241,14 @@ fn process_cell(
stack.add_var(*var_id, val.clone());
}
}
match eval_block(engine_state, stack, block, val.into_pipeline_data()) {
match eval_block(
engine_state,
stack,
block,
val.into_pipeline_data(),
redirect_stdout,
redirect_stderr,
) {
Ok(pd) => pd.into_value(span),
Err(e) => Value::Error { error: e },
}

View File

@ -1,4 +1,4 @@
use nu_engine::{eval_block_with_redirect, CallExt};
use nu_engine::{eval_block, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack};
use nu_protocol::{
@ -41,6 +41,9 @@ impl Command for Where {
let ctrlc = engine_state.ctrlc.clone();
let engine_state = engine_state.clone();
let redirect_stdout = call.redirect_stdout;
let redirect_stderr = call.redirect_stderr;
Ok(input
.into_iter()
.filter_map(move |value| {
@ -49,11 +52,13 @@ impl Command for Where {
stack.add_var(*var_id, value.clone());
}
}
let result = eval_block_with_redirect(
let result = eval_block(
&engine_state,
&mut stack,
&block,
PipelineData::new(span),
redirect_stdout,
redirect_stderr,
);
match result {

View File

@ -39,6 +39,9 @@ impl Command for Benchmark {
let capture_block: CaptureBlock = call.req(engine_state, stack, 0)?;
let block = engine_state.get_block(capture_block.block_id);
let redirect_stdout = call.redirect_stdout;
let redirect_stderr = call.redirect_stderr;
let mut stack = stack.captures_to_stack(&capture_block.captures);
let start_time = Instant::now();
eval_block(
@ -46,6 +49,8 @@ impl Command for Benchmark {
&mut stack,
block,
PipelineData::new(call.head),
redirect_stdout,
redirect_stderr,
)?
.into_value(call.head);

View File

@ -84,7 +84,8 @@ fn exec(
name,
args,
env_vars,
last_expression: true,
redirect_stdout: true,
redirect_stderr: false,
};
let mut command = external_command.spawn_simple_command(&cwd.to_string_lossy().to_string())?;

View File

@ -36,7 +36,8 @@ impl Command for External {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("run-external")
.switch("last-expression", "last-expression", None)
.switch("redirect-stdout", "redirect-stdout", None)
.switch("redirect-stderr", "redirect-stderr", None)
.rest("rest", SyntaxShape::Any, "external command to run")
.category(Category::System)
}
@ -50,7 +51,8 @@ impl Command for External {
) -> Result<PipelineData, ShellError> {
let name: Spanned<String> = call.req(engine_state, stack, 0)?;
let args: Vec<Value> = call.rest(engine_state, stack, 1)?;
let last_expression = call.has_flag("last-expression");
let redirect_stdout = call.has_flag("redirect-stdout");
let redirect_stderr = call.has_flag("redirect-stderr");
// Translate environment variables from Values to Strings
let config = stack.get_config().unwrap_or_default();
@ -93,7 +95,8 @@ impl Command for External {
let command = ExternalCommand {
name,
args: args_strs,
last_expression,
redirect_stdout,
redirect_stderr,
env_vars: env_vars_str,
};
command.run_with_input(engine_state, stack, input)
@ -103,7 +106,8 @@ impl Command for External {
pub struct ExternalCommand {
pub name: Spanned<String>,
pub args: Vec<Spanned<String>>,
pub last_expression: bool,
pub redirect_stdout: bool,
pub redirect_stderr: bool,
pub env_vars: HashMap<String, String>,
}
@ -138,10 +142,14 @@ impl ExternalCommand {
// If the external is not the last command, its output will get piped
// either as a string or binary
if !self.last_expression {
if self.redirect_stdout {
process.stdout(Stdio::piped());
}
if self.redirect_stderr {
process.stderr(Stdio::piped());
}
// If there is an input from the pipeline. The stdin from the process
// is piped so it can be used to send the input information
if !matches!(input, PipelineData::Value(Value::Nothing { .. }, ..)) {
@ -173,10 +181,14 @@ impl ExternalCommand {
// If the external is not the last command, its output will get piped
// either as a string or binary
if !self.last_expression {
if self.redirect_stdout {
process.stdout(Stdio::piped());
}
if self.redirect_stderr {
process.stderr(Stdio::piped());
}
// If there is an input from the pipeline. The stdin from the process
// is piped so it can be used to send the input information
if !matches!(input, PipelineData::Value(Value::Nothing { .. }, ..)) {
@ -241,7 +253,8 @@ impl ExternalCommand {
}
}
let last_expression = self.last_expression;
let redirect_stdout = self.redirect_stdout;
let redirect_stderr = self.redirect_stderr;
let span = self.name.span;
let output_ctrlc = ctrlc.clone();
let (tx, rx) = mpsc::channel();
@ -249,7 +262,12 @@ impl ExternalCommand {
std::thread::spawn(move || {
// If this external is not the last expression, then its output is piped to a channel
// and we create a ValueStream that can be consumed
if !last_expression {
if redirect_stderr {
let _ = child.stderr.take();
}
if redirect_stdout {
let stdout = child.stdout.take().ok_or_else(|| {
ShellError::ExternalCommand(
"Error taking stdout from external".to_string(),