forked from extern/nushell
Support redirect stderr and stdout+stderr with a pipe (#11708)
# Description Close: #9673 Close: #8277 Close: #10944 This pr introduces the following syntax: 1. `e>|`, pipe stderr to next command. Example: `$env.FOO=bar nu --testbin echo_env_stderr FOO e>| str length` 2. `o+e>|` and `e+o>|`, pipe both stdout and stderr to next command, example: `$env.FOO=bar nu --testbin echo_env_mixed out-err FOO FOO e+o>| str length` Note: it only works for external commands. ~There is no different for internal commands, that is, the following three commands do the same things:~ Edit: it raises errors if we want to pipes for internal commands ``` ❯ ls e>| str length Error: × `e>|` only works with external streams ╭─[entry #1:1:1] 1 │ ls e>| str length · ─┬─ · ╰── `e>|` only works on external streams ╰──── ❯ ls e+o>| str length Error: × `o+e>|` only works with external streams ╭─[entry #2:1:1] 1 │ ls e+o>| str length · ──┬── · ╰── `o+e>|` only works on external streams ╰──── ``` This can help us to avoid some strange issues like the following: `$env.FOO=bar (nu --testbin echo_env_stderr FOO) e>| str length` Which is hard to understand and hard to explain to users. # User-Facing Changes Nan # Tests + Formatting To be done # After Submitting Maybe update documentation about these syntax.
This commit is contained in:
@ -37,6 +37,12 @@ impl LiteCommand {
|
||||
#[derive(Debug)]
|
||||
pub enum LiteElement {
|
||||
Command(Option<Span>, LiteCommand),
|
||||
// Similar to LiteElement::Command, except the previous command's output is stderr piped.
|
||||
// e.g: `e>| cmd`
|
||||
ErrPipedCommand(Option<Span>, LiteCommand),
|
||||
// Similar to LiteElement::Command, except the previous command's output is stderr + stdout piped.
|
||||
// e.g: `o+e>| cmd`
|
||||
OutErrPipedCommand(Option<Span>, LiteCommand),
|
||||
// final field indicates if it's in append mode
|
||||
Redirection(Span, Redirection, LiteCommand, bool),
|
||||
// SeparateRedirection variant can only be generated by two different Redirection variant
|
||||
@ -272,7 +278,9 @@ pub fn lite_parse(tokens: &[Token]) -> (LiteBlock, Option<ParseError>) {
|
||||
last_connector = token.contents;
|
||||
last_connector_span = Some(token.span);
|
||||
}
|
||||
TokenContents::Pipe => {
|
||||
pipe_token @ (TokenContents::Pipe
|
||||
| TokenContents::ErrGreaterPipe
|
||||
| TokenContents::OutErrGreaterPipe) => {
|
||||
if let Some(err) = push_command_to(
|
||||
&mut curr_pipeline,
|
||||
curr_command,
|
||||
@ -283,8 +291,8 @@ pub fn lite_parse(tokens: &[Token]) -> (LiteBlock, Option<ParseError>) {
|
||||
}
|
||||
|
||||
curr_command = LiteCommand::new();
|
||||
last_token = TokenContents::Pipe;
|
||||
last_connector = TokenContents::Pipe;
|
||||
last_token = *pipe_token;
|
||||
last_connector = *pipe_token;
|
||||
last_connector_span = Some(token.span);
|
||||
}
|
||||
TokenContents::Eol => {
|
||||
@ -429,7 +437,35 @@ fn push_command_to(
|
||||
is_append_mode,
|
||||
))
|
||||
}
|
||||
None => pipeline.push(LiteElement::Command(last_connector_span, command)),
|
||||
None => {
|
||||
if last_connector == TokenContents::ErrGreaterPipe {
|
||||
pipeline.push(LiteElement::ErrPipedCommand(last_connector_span, command))
|
||||
} else if last_connector == TokenContents::OutErrGreaterPipe {
|
||||
// Don't allow o+e>| along with redirection.
|
||||
for cmd in &pipeline.commands {
|
||||
if matches!(
|
||||
cmd,
|
||||
LiteElement::Redirection { .. }
|
||||
| LiteElement::SameTargetRedirection { .. }
|
||||
| LiteElement::SeparateRedirection { .. }
|
||||
) {
|
||||
return Some(ParseError::LabeledError(
|
||||
"`o+e>|` pipe is not allowed to use with redirection".into(),
|
||||
"try to use different type of pipe, or remove redirection".into(),
|
||||
last_connector_span
|
||||
.expect("internal error: outerr pipe missing span information"),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
pipeline.push(LiteElement::OutErrPipedCommand(
|
||||
last_connector_span,
|
||||
command,
|
||||
))
|
||||
} else {
|
||||
pipeline.push(LiteElement::Command(last_connector_span, command))
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
} else if get_redirection(last_connector).is_some() {
|
||||
|
Reference in New Issue
Block a user