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:
Wind
2024-02-09 01:30:46 +08:00
committed by GitHub
parent e7f1bf8535
commit 58c6fea60b
15 changed files with 520 additions and 106 deletions

View File

@ -142,6 +142,22 @@ fn command_substitution_wont_output_extra_newline() {
assert_eq!(actual.out, "bar");
}
#[test]
fn basic_err_pipe_works() {
let actual = nu!(r#"with-env [FOO "bar"] { nu --testbin echo_env_stderr FOO e>| str length }"#);
// there is a `newline` output from nu --testbin
assert_eq!(actual.out, "4");
}
#[test]
fn basic_outerr_pipe_works() {
let actual = nu!(
r#"with-env [FOO "bar"] { nu --testbin echo_env_mixed out-err FOO FOO o+e>| str length }"#
);
// there is a `newline` output from nu --testbin
assert_eq!(actual.out, "8");
}
mod it_evaluation {
use super::nu;
use nu_test_support::fs::Stub::{EmptyFile, FileWithContent, FileWithContentToBeTrimmed};

View File

@ -1113,6 +1113,18 @@ fn pipe_input_to_print() {
assert!(actual.err.is_empty());
}
#[test]
fn err_pipe_input_to_print() {
let actual = nu!(r#""foo" e>| print"#);
assert!(actual.err.contains("only works on external streams"));
}
#[test]
fn outerr_pipe_input_to_print() {
let actual = nu!(r#""foo" o+e>| print"#);
assert!(actual.err.contains("only works on external streams"));
}
#[test]
fn command_not_found_error_shows_not_found_2() {
let actual = nu!(r#"