mirror of
https://github.com/nushell/nushell.git
synced 2025-04-03 06:01:11 +02:00
Make pipe redirections consistent, add err>|
etc. forms (#13334)
# Description Fixes the lexer to recognize `out>|`, `err>|`, `out+err>|`, etc. Previously only the short-style forms were recognized, which was inconsistent with normal file redirections. I also integrated it all more into the normal lex path by checking `|` in a special way, which should be more performant and consistent, and cleans up the code a bunch. Closes #13331. # User-Facing Changes - Adds `out>|` (error), `err>|`, `out+err>|`, `err+out>|` as recognized forms of the pipe redirection. # Tests + Formatting All passing. Added tests for the new forms. # After Submitting - [ ] release notes
This commit is contained in:
parent
616e9faaf1
commit
ea8c4e3af2
@ -238,6 +238,10 @@ pub fn lex_item(
|
|||||||
Some(e),
|
Some(e),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else if c == b'|' && is_redirection(&input[token_start..*curr_offset]) {
|
||||||
|
// matches err>| etc.
|
||||||
|
*curr_offset += 1;
|
||||||
|
break;
|
||||||
} else if is_item_terminator(&block_level, c, additional_whitespace, special_tokens) {
|
} else if is_item_terminator(&block_level, c, additional_whitespace, special_tokens) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -301,6 +305,16 @@ pub fn lex_item(
|
|||||||
contents: TokenContents::OutGreaterGreaterThan,
|
contents: TokenContents::OutGreaterGreaterThan,
|
||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
|
b"out>|" | b"o>|" => {
|
||||||
|
err = Some(ParseError::Expected(
|
||||||
|
"`|`. Redirecting stdout to a pipe is the same as normal piping.",
|
||||||
|
span,
|
||||||
|
));
|
||||||
|
Token {
|
||||||
|
contents: TokenContents::Item,
|
||||||
|
span,
|
||||||
|
}
|
||||||
|
}
|
||||||
b"err>" | b"e>" => Token {
|
b"err>" | b"e>" => Token {
|
||||||
contents: TokenContents::ErrGreaterThan,
|
contents: TokenContents::ErrGreaterThan,
|
||||||
span,
|
span,
|
||||||
@ -309,6 +323,10 @@ pub fn lex_item(
|
|||||||
contents: TokenContents::ErrGreaterGreaterThan,
|
contents: TokenContents::ErrGreaterGreaterThan,
|
||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
|
b"err>|" | b"e>|" => Token {
|
||||||
|
contents: TokenContents::ErrGreaterPipe,
|
||||||
|
span,
|
||||||
|
},
|
||||||
b"out+err>" | b"err+out>" | b"o+e>" | b"e+o>" => Token {
|
b"out+err>" | b"err+out>" | b"o+e>" | b"e+o>" => Token {
|
||||||
contents: TokenContents::OutErrGreaterThan,
|
contents: TokenContents::OutErrGreaterThan,
|
||||||
span,
|
span,
|
||||||
@ -317,6 +335,10 @@ pub fn lex_item(
|
|||||||
contents: TokenContents::OutErrGreaterGreaterThan,
|
contents: TokenContents::OutErrGreaterGreaterThan,
|
||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
|
b"out+err>|" | b"err+out>|" | b"o+e>|" | b"e+o>|" => Token {
|
||||||
|
contents: TokenContents::OutErrGreaterPipe,
|
||||||
|
span,
|
||||||
|
},
|
||||||
b"&&" => {
|
b"&&" => {
|
||||||
err = Some(ParseError::ShellAndAnd(span));
|
err = Some(ParseError::ShellAndAnd(span));
|
||||||
Token {
|
Token {
|
||||||
@ -580,17 +602,6 @@ fn lex_internal(
|
|||||||
// If the next character is non-newline whitespace, skip it.
|
// If the next character is non-newline whitespace, skip it.
|
||||||
curr_offset += 1;
|
curr_offset += 1;
|
||||||
} else {
|
} else {
|
||||||
let (token, err) = try_lex_special_piped_item(input, &mut curr_offset, span_offset);
|
|
||||||
if error.is_none() {
|
|
||||||
error = err;
|
|
||||||
}
|
|
||||||
if let Some(token) = token {
|
|
||||||
output.push(token);
|
|
||||||
is_complete = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, try to consume an unclassified token.
|
|
||||||
let (token, err) = lex_item(
|
let (token, err) = lex_item(
|
||||||
input,
|
input,
|
||||||
&mut curr_offset,
|
&mut curr_offset,
|
||||||
@ -609,68 +620,10 @@ fn lex_internal(
|
|||||||
(output, error)
|
(output, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// trying to lex for the following item:
|
/// True if this the start of a redirection. Does not match `>>` or `>|` forms.
|
||||||
/// e>|, e+o>|, o+e>|
|
fn is_redirection(token: &[u8]) -> bool {
|
||||||
///
|
matches!(
|
||||||
/// It returns Some(token) if we find the item, or else return None.
|
token,
|
||||||
fn try_lex_special_piped_item(
|
b"o>" | b"out>" | b"e>" | b"err>" | b"o+e>" | b"e+o>" | b"out+err>" | b"err+out>"
|
||||||
input: &[u8],
|
)
|
||||||
curr_offset: &mut usize,
|
|
||||||
span_offset: usize,
|
|
||||||
) -> (Option<Token>, Option<ParseError>) {
|
|
||||||
let c = input[*curr_offset];
|
|
||||||
let e_pipe_len = 3;
|
|
||||||
let eo_pipe_len = 5;
|
|
||||||
let o_pipe_len = 3;
|
|
||||||
let offset = *curr_offset;
|
|
||||||
if c == b'e' {
|
|
||||||
// expect `e>|`
|
|
||||||
if (offset + e_pipe_len <= input.len()) && (&input[offset..offset + e_pipe_len] == b"e>|") {
|
|
||||||
*curr_offset += e_pipe_len;
|
|
||||||
return (
|
|
||||||
Some(Token::new(
|
|
||||||
TokenContents::ErrGreaterPipe,
|
|
||||||
Span::new(span_offset + offset, span_offset + offset + e_pipe_len),
|
|
||||||
)),
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (offset + eo_pipe_len <= input.len())
|
|
||||||
&& (&input[offset..offset + eo_pipe_len] == b"e+o>|")
|
|
||||||
{
|
|
||||||
*curr_offset += eo_pipe_len;
|
|
||||||
return (
|
|
||||||
Some(Token::new(
|
|
||||||
TokenContents::OutErrGreaterPipe,
|
|
||||||
Span::new(span_offset + offset, span_offset + offset + eo_pipe_len),
|
|
||||||
)),
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if c == b'o' {
|
|
||||||
// indicates an error if user happened to type `o>|`
|
|
||||||
if offset + o_pipe_len <= input.len() && (&input[offset..offset + o_pipe_len] == b"o>|") {
|
|
||||||
return (
|
|
||||||
None,
|
|
||||||
Some(ParseError::Expected(
|
|
||||||
"`|`. Redirecting stdout to a pipe is the same as normal piping.",
|
|
||||||
Span::new(span_offset + offset, span_offset + offset + o_pipe_len),
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// it can be the following case: `o+e>|`
|
|
||||||
if (offset + eo_pipe_len <= input.len())
|
|
||||||
&& (&input[offset..offset + eo_pipe_len] == b"o+e>|")
|
|
||||||
{
|
|
||||||
*curr_offset += eo_pipe_len;
|
|
||||||
return (
|
|
||||||
Some(Token::new(
|
|
||||||
TokenContents::OutErrGreaterPipe,
|
|
||||||
Span::new(span_offset + offset, span_offset + offset + eo_pipe_len),
|
|
||||||
)),
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(None, None)
|
|
||||||
}
|
}
|
||||||
|
@ -149,17 +149,26 @@ fn command_substitution_wont_output_extra_newline() {
|
|||||||
assert_eq!(actual.out, "bar");
|
assert_eq!(actual.out, "bar");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[rstest::rstest]
|
||||||
fn basic_err_pipe_works() {
|
#[case("err>|")]
|
||||||
let actual =
|
#[case("e>|")]
|
||||||
nu!(r#"with-env { FOO: "bar" } { nu --testbin echo_env_stderr FOO e>| str length }"#);
|
fn basic_err_pipe_works(#[case] redirection: &str) {
|
||||||
|
let actual = nu!(
|
||||||
|
r#"with-env { FOO: "bar" } { nu --testbin echo_env_stderr FOO {redirection} str length }"#
|
||||||
|
.replace("{redirection}", redirection)
|
||||||
|
);
|
||||||
assert_eq!(actual.out, "3");
|
assert_eq!(actual.out, "3");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[rstest::rstest]
|
||||||
fn basic_outerr_pipe_works() {
|
#[case("out+err>|")]
|
||||||
|
#[case("err+out>|")]
|
||||||
|
#[case("o+e>|")]
|
||||||
|
#[case("e+o>|")]
|
||||||
|
fn basic_outerr_pipe_works(#[case] redirection: &str) {
|
||||||
let actual = nu!(
|
let actual = nu!(
|
||||||
r#"with-env { FOO: "bar" } { nu --testbin echo_env_mixed out-err FOO FOO o+e>| str length }"#
|
r#"with-env { FOO: "bar" } { nu --testbin echo_env_mixed out-err FOO FOO {redirection} str length }"#
|
||||||
|
.replace("{redirection}", redirection)
|
||||||
);
|
);
|
||||||
assert_eq!(actual.out, "7");
|
assert_eq!(actual.out, "7");
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user