mirror of
https://github.com/nushell/nushell
synced 2024-11-10 15:14:14 +00:00
Add pipeline redirection support (#4594)
* redirection * Remove commented-out * fix tests * more fixes
This commit is contained in:
parent
739e403cd5
commit
9888f8f298
46 changed files with 463 additions and 288 deletions
|
@ -278,6 +278,8 @@ impl NuCompleter {
|
|||
&mut stack,
|
||||
&block,
|
||||
PipelineData::new(new_span),
|
||||
true,
|
||||
true,
|
||||
);
|
||||
|
||||
let v: Vec<_> = match result {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) => {
|
||||
|
|
5
crates/nu-command/src/env/let_env.rs
vendored
5
crates/nu-command/src/env/let_env.rs
vendored
|
@ -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)?;
|
||||
|
|
9
crates/nu-command/src/env/with_env.rs
vendored
9
crates/nu-command/src/env/with_env.rs
vendored
|
@ -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)]
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -50,6 +50,8 @@ impl Command for Collect {
|
|||
&mut stack,
|
||||
&block,
|
||||
PipelineData::new(call.head),
|
||||
call.redirect_stdout,
|
||||
call.redirect_stderr,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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| {
|
||||
|
|
|
@ -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 },
|
||||
}
|
||||
|
|
|
@ -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 },
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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 },
|
||||
};
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 },
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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())?;
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -354,3 +354,15 @@ fn str_reverse() {
|
|||
|
||||
assert!(actual.out.contains("llehsun"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_redirection_trim() {
|
||||
let actual = nu!(
|
||||
cwd: ".", pipeline(
|
||||
r#"
|
||||
let x = (nu --testbin cococo niceone); $x | str trim | str length
|
||||
"#
|
||||
));
|
||||
|
||||
assert_eq!(actual.out, "7");
|
||||
}
|
||||
|
|
|
@ -55,6 +55,16 @@ fn with_env_and_shorthand_same_result() {
|
|||
assert_eq!(actual_shorthand.out, actual_normal.out);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_redirection2() {
|
||||
let actual = nu!(
|
||||
cwd: "tests/fixtures/formats",
|
||||
"let x = (FOO=BAR nu --testbin cococo niceenvvar); $x | str trim | str length"
|
||||
);
|
||||
|
||||
assert_eq!(actual.out, "10");
|
||||
}
|
||||
|
||||
// FIXME: jt: needs more work
|
||||
#[ignore]
|
||||
#[test]
|
||||
|
|
|
@ -161,8 +161,14 @@ fn get_converted_value(
|
|||
stack.add_var(*var_id, orig_val.clone());
|
||||
}
|
||||
|
||||
let result =
|
||||
eval_block(engine_state, &mut stack, block, PipelineData::new(val_span));
|
||||
let result = eval_block(
|
||||
engine_state,
|
||||
&mut stack,
|
||||
block,
|
||||
PipelineData::new(val_span),
|
||||
true,
|
||||
true,
|
||||
);
|
||||
|
||||
match result {
|
||||
Ok(data) => ConversionResult::Ok(data.into_value(val_span)),
|
||||
|
|
|
@ -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)?
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ pub use column::get_columns;
|
|||
pub use documentation::{generate_docs, get_brief_help, get_documentation, get_full_help};
|
||||
pub use env::*;
|
||||
pub use eval::{
|
||||
eval_block, eval_block_with_redirect, eval_expression, eval_expression_with_input,
|
||||
eval_operator, eval_subexpression,
|
||||
eval_block, eval_expression, eval_expression_with_input, eval_operator, eval_subexpression,
|
||||
};
|
||||
pub use glob_from::glob_from;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use nu_protocol::ast::Expr;
|
||||
use nu_protocol::engine::{EngineState, Stack, StateWorkingSet};
|
||||
use nu_protocol::PipelineData;
|
||||
use nu_protocol::{ast::Call, engine::Command, ShellError, Signature};
|
||||
use nu_protocol::{PipelineData, Spanned};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct KnownExternal {
|
||||
|
@ -61,15 +61,25 @@ impl Command for KnownExternal {
|
|||
call.positional.push(arg.clone())
|
||||
}
|
||||
|
||||
// if last_expression {
|
||||
// call.named.push((
|
||||
// Spanned {
|
||||
// item: "last-expression".into(),
|
||||
// span: head.span,
|
||||
// },
|
||||
// None,
|
||||
// ))
|
||||
// }
|
||||
if call.redirect_stdout {
|
||||
call.named.push((
|
||||
Spanned {
|
||||
item: "redirect-stdout".into(),
|
||||
span: call_span,
|
||||
},
|
||||
None,
|
||||
))
|
||||
}
|
||||
|
||||
if call.redirect_stderr {
|
||||
call.named.push((
|
||||
Spanned {
|
||||
item: "redirect-stderr".into(),
|
||||
span: call_span,
|
||||
},
|
||||
None,
|
||||
))
|
||||
}
|
||||
|
||||
command.run(engine_state, stack, &call, input)
|
||||
}
|
||||
|
|
|
@ -579,6 +579,8 @@ pub fn parse_export(
|
|||
decl_id: export_decl_id,
|
||||
positional: vec![],
|
||||
named: vec![],
|
||||
redirect_stdout: true,
|
||||
redirect_stderr: false,
|
||||
});
|
||||
|
||||
let exportable = if let Some(kw_span) = spans.get(1) {
|
||||
|
@ -986,6 +988,8 @@ pub fn parse_module(
|
|||
decl_id: module_decl_id,
|
||||
positional: vec![module_name_expr, block_expr],
|
||||
named: vec![],
|
||||
redirect_stdout: true,
|
||||
redirect_stderr: false,
|
||||
});
|
||||
|
||||
(
|
||||
|
@ -1200,6 +1204,8 @@ pub fn parse_use(
|
|||
decl_id: use_decl_id,
|
||||
positional: vec![import_pattern_expr],
|
||||
named: vec![],
|
||||
redirect_stdout: true,
|
||||
redirect_stderr: false,
|
||||
});
|
||||
|
||||
(
|
||||
|
@ -1399,6 +1405,8 @@ pub fn parse_hide(
|
|||
decl_id: hide_decl_id,
|
||||
positional: vec![import_pattern_expr],
|
||||
named: vec![],
|
||||
redirect_stdout: true,
|
||||
redirect_stderr: false,
|
||||
});
|
||||
|
||||
(
|
||||
|
@ -1480,6 +1488,8 @@ pub fn parse_let(
|
|||
head: spans[0],
|
||||
positional: vec![lvalue, rvalue],
|
||||
named: vec![],
|
||||
redirect_stdout: true,
|
||||
redirect_stderr: false,
|
||||
});
|
||||
|
||||
return (
|
||||
|
|
|
@ -3420,7 +3420,7 @@ pub fn parse_expression(
|
|||
(
|
||||
Expression {
|
||||
expr: Expr::String(String::new()),
|
||||
span: spans[pos],
|
||||
span: Span { start: 0, end: 0 },
|
||||
ty: Type::Nothing,
|
||||
custom_completion: None,
|
||||
},
|
||||
|
@ -3556,6 +3556,8 @@ pub fn parse_expression(
|
|||
decl_id,
|
||||
named: vec![],
|
||||
positional,
|
||||
redirect_stdout: true,
|
||||
redirect_stderr: false,
|
||||
}));
|
||||
|
||||
(
|
||||
|
@ -4108,6 +4110,8 @@ fn wrap_expr_with_collect(working_set: &mut StateWorkingSet, expr: &Expression)
|
|||
named: vec![],
|
||||
positional: output,
|
||||
decl_id,
|
||||
redirect_stdout: true,
|
||||
redirect_stderr: false,
|
||||
})),
|
||||
span,
|
||||
ty: Type::String,
|
||||
|
|
|
@ -8,6 +8,8 @@ pub struct Call {
|
|||
pub head: Span,
|
||||
pub positional: Vec<Expression>,
|
||||
pub named: Vec<(Spanned<String>, Option<Expression>)>,
|
||||
pub redirect_stdout: bool,
|
||||
pub redirect_stderr: bool,
|
||||
}
|
||||
|
||||
impl Call {
|
||||
|
@ -17,6 +19,8 @@ impl Call {
|
|||
head,
|
||||
positional: vec![],
|
||||
named: vec![],
|
||||
redirect_stdout: true,
|
||||
redirect_stderr: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ pub(crate) fn evaluate(
|
|||
std::process::exit(1);
|
||||
}
|
||||
|
||||
match eval_block(engine_state, &mut stack, &block, input) {
|
||||
match eval_block(engine_state, &mut stack, &block, input, false, false) {
|
||||
Ok(pipeline_data) => {
|
||||
crate::eval_file::print_table_or_error(engine_state, &mut stack, pipeline_data, &config)
|
||||
}
|
||||
|
|
|
@ -251,3 +251,9 @@ fn with_env_shorthand_nested_quotes() -> TestResult {
|
|||
"-arg \"hello world\"",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_redirection_stderr() -> TestResult {
|
||||
// try a nonsense binary
|
||||
run_test(r#"do -i { asdjw4j5cnaabw44rd }; echo done"#, "done")
|
||||
}
|
||||
|
|
|
@ -226,7 +226,7 @@ pub(crate) fn eval_source(
|
|||
report_error(&working_set, &err);
|
||||
}
|
||||
|
||||
match eval_block(engine_state, stack, &block, input) {
|
||||
match eval_block(engine_state, stack, &block, input, false, false) {
|
||||
Ok(pipeline_data) => {
|
||||
if let Err(err) = print_pipeline_data(pipeline_data, engine_state, stack) {
|
||||
let working_set = StateWorkingSet::new(engine_state);
|
||||
|
|
Loading…
Reference in a new issue