mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 04:45:04 +02:00
Stdout/Stderr redirection (#7185)
This adds new pipeline connectors called out> and err> which redirect either stdout or stderr to a file. You can also use out+err> (or err+out>) to redirect both streams into a file.
This commit is contained in:
@ -2,8 +2,8 @@ use crate::{current_dir_str, get_full_help, scope::create_scope};
|
||||
use nu_path::expand_path_with;
|
||||
use nu_protocol::{
|
||||
ast::{
|
||||
Assignment, Bits, Block, Boolean, Call, Comparison, Expr, Expression, Math, Operator,
|
||||
PathMember, PipelineElement,
|
||||
Argument, Assignment, Bits, Block, Boolean, Call, Comparison, Expr, Expression, Math,
|
||||
Operator, PathMember, PipelineElement, Redirection,
|
||||
},
|
||||
engine::{EngineState, Stack},
|
||||
Config, HistoryFileFormat, IntoInterruptiblePipelineData, IntoPipelineData, ListStream,
|
||||
@ -801,7 +801,7 @@ pub fn eval_element_with_input(
|
||||
redirect_stderr: bool,
|
||||
) -> Result<(PipelineData, bool), ShellError> {
|
||||
match element {
|
||||
PipelineElement::Expression(expr) => eval_expression_with_input(
|
||||
PipelineElement::Expression(_, expr) => eval_expression_with_input(
|
||||
engine_state,
|
||||
stack,
|
||||
expr,
|
||||
@ -809,7 +809,98 @@ pub fn eval_element_with_input(
|
||||
redirect_stdout,
|
||||
redirect_stderr,
|
||||
),
|
||||
PipelineElement::Redirect(expr) => eval_expression_with_input(
|
||||
PipelineElement::Redirection(span, redirection, expr) => match &expr.expr {
|
||||
Expr::String(_) => {
|
||||
let input = match (redirection, input) {
|
||||
(
|
||||
Redirection::Stderr,
|
||||
PipelineData::ExternalStream {
|
||||
stderr,
|
||||
exit_code,
|
||||
span,
|
||||
metadata,
|
||||
..
|
||||
},
|
||||
) => PipelineData::ExternalStream {
|
||||
stdout: stderr,
|
||||
stderr: None,
|
||||
exit_code,
|
||||
span,
|
||||
metadata,
|
||||
},
|
||||
(
|
||||
Redirection::StdoutAndStderr,
|
||||
PipelineData::ExternalStream {
|
||||
stdout,
|
||||
stderr,
|
||||
exit_code,
|
||||
span,
|
||||
metadata,
|
||||
},
|
||||
) => match (stdout, stderr) {
|
||||
(Some(stdout), Some(stderr)) => PipelineData::ExternalStream {
|
||||
stdout: Some(stdout.chain(stderr)),
|
||||
stderr: None,
|
||||
exit_code,
|
||||
span,
|
||||
metadata,
|
||||
},
|
||||
(None, Some(stderr)) => PipelineData::ExternalStream {
|
||||
stdout: Some(stderr),
|
||||
stderr: None,
|
||||
exit_code,
|
||||
span,
|
||||
metadata,
|
||||
},
|
||||
(Some(stdout), None) => PipelineData::ExternalStream {
|
||||
stdout: Some(stdout),
|
||||
stderr: None,
|
||||
exit_code,
|
||||
span,
|
||||
metadata,
|
||||
},
|
||||
(None, None) => PipelineData::ExternalStream {
|
||||
stdout: None,
|
||||
stderr: None,
|
||||
exit_code,
|
||||
span,
|
||||
metadata,
|
||||
},
|
||||
},
|
||||
(_, input) => input,
|
||||
};
|
||||
|
||||
if let Some(save_command) = engine_state.find_decl(b"save", &[]) {
|
||||
eval_call(
|
||||
engine_state,
|
||||
stack,
|
||||
&Call {
|
||||
decl_id: save_command,
|
||||
head: *span,
|
||||
arguments: vec![
|
||||
Argument::Positional(expr.clone()),
|
||||
Argument::Named((
|
||||
Spanned {
|
||||
item: "--raw".into(),
|
||||
span: *span,
|
||||
},
|
||||
None,
|
||||
None,
|
||||
)),
|
||||
],
|
||||
redirect_stdout: false,
|
||||
redirect_stderr: false,
|
||||
},
|
||||
input,
|
||||
)
|
||||
.map(|x| (x, false))
|
||||
} else {
|
||||
Err(ShellError::CommandNotFound(*span))
|
||||
}
|
||||
}
|
||||
_ => Err(ShellError::CommandNotFound(*span)),
|
||||
},
|
||||
PipelineElement::And(_, expr) => eval_expression_with_input(
|
||||
engine_state,
|
||||
stack,
|
||||
expr,
|
||||
@ -817,15 +908,7 @@ pub fn eval_element_with_input(
|
||||
redirect_stdout,
|
||||
redirect_stderr,
|
||||
),
|
||||
PipelineElement::And(expr) => eval_expression_with_input(
|
||||
engine_state,
|
||||
stack,
|
||||
expr,
|
||||
input,
|
||||
redirect_stdout,
|
||||
redirect_stderr,
|
||||
),
|
||||
PipelineElement::Or(expr) => eval_expression_with_input(
|
||||
PipelineElement::Or(_, expr) => eval_expression_with_input(
|
||||
engine_state,
|
||||
stack,
|
||||
expr,
|
||||
@ -846,15 +929,23 @@ pub fn eval_block(
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let num_pipelines = block.len();
|
||||
for (pipeline_idx, pipeline) in block.pipelines.iter().enumerate() {
|
||||
for (i, elem) in pipeline.elements.iter().enumerate() {
|
||||
let mut i = 0;
|
||||
|
||||
while i < pipeline.elements.len() {
|
||||
// if eval internal command failed, it can just make early return with `Err(ShellError)`.
|
||||
let eval_result = eval_element_with_input(
|
||||
engine_state,
|
||||
stack,
|
||||
elem,
|
||||
&pipeline.elements[i],
|
||||
input,
|
||||
redirect_stdout || (i != pipeline.elements.len() - 1),
|
||||
redirect_stderr,
|
||||
redirect_stderr
|
||||
|| ((i < pipeline.elements.len() - 1)
|
||||
&& (matches!(
|
||||
pipeline.elements[i + 1],
|
||||
PipelineElement::Redirection(_, Redirection::Stderr, _)
|
||||
| PipelineElement::Redirection(_, Redirection::StdoutAndStderr, _)
|
||||
))),
|
||||
)?;
|
||||
input = eval_result.0;
|
||||
// external command may runs to failed
|
||||
@ -863,6 +954,8 @@ pub fn eval_block(
|
||||
if eval_result.1 {
|
||||
return Ok(input);
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if pipeline_idx < (num_pipelines) - 1 {
|
||||
|
Reference in New Issue
Block a user