Pipelines
The pipeline special variable $in
Best practices for pipeline commands
Interaction with Unix pipes
Handling stdout and stderr
You can handle stderr in multiple ways:
- Do nothing, stderr will be printed directly
- Pipe stderr to the next command, using
e>|
ore+o>|
- Redirect stderr to a file, using
e> file_path
, ore+o> file_path
- Use
do -i { cmd } | complete
to capture both stdout and stderr as structured data
For the next examples, let's assume this file:
# demo.nu
print "foo"
print -e "barbar"
It prints foo
to stdout and barbar
to stderr. The following table illustrates the differences between the different redirection styles:
Redirection to a pipeline:
type | command | $result contents | printed to terminal |
---|---|---|---|
| | let result = nu demo.nu | str upcase | "FOO" | "barbar" |
e>| | let result = nu demo.nu e>| str upcase | "BARBAR" | "foo" |
o+e>| | let result = nu demo.nu e+o>| str upcase | "FOO\nBARBAR" | nothing |
Redirection to a file:
type | command | file.txt contents | printed to terminal |
---|---|---|---|
o> file_path | nu demo.nu o> file.txt | "foo" | "barbar" |
e> file_path | nu demo.nu e> file.txt | "barbar" | "foo" |
o+e> file_path | nu demo.nu o+e> file.txt | "foo/nbarbar" | nothing |
complete
command:
type | command | $result contents |
---|---|---|
use complete | let result = do { nu demo.nu } | complete | record containing both stdout and stderr |
Note that e>|
and o+e>|
only work with external command, if you pipe internal commands' output through e>|
and o+e>|
, you will get an error:
❯ 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
╰────
You can also redirect stdout
to a file, and pipe stderr
to next command:
nu demo.nu o> file.txt e>| str upcase
nu demo.nu e> file.txt | str upcase
But you can't use redirection along with o+e>|
, because it's ambiguous:
nu demo.nu o> file.txt o+e>| str upcase
Also note that complete
is special, it doesn't work with e>|
, o+e>|
.
Stdio and redirection behavior examples
Pipeline and redirection behavior can be hard to follow when they are used with subexpressions, or custom commands. Here are some examples that show intended stdio behavior.
Examples for subexpression
- (^cmd1 | ^cmd2; ^cmd3 | ^cmd4)
Command | Stdout | Stderr |
---|---|---|
cmd1 | Piped | Terminal |
cmd2 | Terminal | Terminal |
cmd3 | Piped | Terminal |
cmd4 | Terminal | Terminal |
- (^cmd1 | ^cmd2; ^cmd3 | ^cmd4) | ^cmd5
It runs (^cmd1 | ^cmd2; ^cmd3 | ^cmd4)
first, then pipes stdout to ^cmd5
, where both stdout and stderr are directed to the Terminal.
Command | Stdout | Stderr |
---|---|---|
cmd1 | Piped | Terminal |
cmd2 | Terminal | Terminal |
cmd3 | Piped | Terminal |
cmd4 | Piped | Terminal |
- (^cmd1 | ^cmd2; ^cmd3 | ^cmd4) e>| ^cmd5
It runs (^cmd1 | ^cmd2; ^cmd3 | ^cmd4)
first, then pipes stderr to ^cmd5
, where both stdout and stderr are directed to the Terminal.
Command | Stdout | Stderr |
---|---|---|
cmd1 | Piped | Terminal |
cmd2 | Terminal | Terminal |
cmd3 | Piped | Terminal |
cmd4 | Terminal | Piped |
- (^cmd1 | ^cmd2; ^cmd3 | ^cmd4) o+e>| ^cmd5
It runs (^cmd1 | ^cmd2; ^cmd3 | ^cmd4)
first, then pipes stdout and stderr to ^cmd5
, where both stdout and stderr are directed to the Terminal.
Command | Stdout | Stderr |
---|---|---|
cmd1 | Piped | Terminal |
cmd2 | Terminal | Terminal |
cmd3 | Piped | Terminal |
cmd4 | Piped | Piped |
- (^cmd1 | ^cmd2; ^cmd3 | ^cmd4) o> test.out
Command | Stdout | Stderr |
---|---|---|
cmd1 | Piped | Terminal |
cmd2 | File | Terminal |
cmd3 | Piped | Terminal |
cmd4 | File | Terminal |
- (^cmd1 | ^cmd2; ^cmd3 | ^cmd4) e> test.out
Command | Stdout | Stderr |
---|---|---|
cmd1 | Piped | File |
cmd2 | Terminal | File |
cmd3 | Piped | File |
cmd4 | Terminal | File |
- (^cmd1 | ^cmd2; ^cmd3 | ^cmd4) o+e> test.out
Command | Stdout | Stderr |
---|---|---|
cmd1 | Piped | File |
cmd2 | File | File |
cmd3 | Piped | File |
cmd4 | File | File |
Examples for custom command
Given the following custom commands
def custom-cmd [] {
^cmd1 | ^cmd2
^cmd3 | ^cmd4
}
The custom command stdio behavior is the same as the previous section.
In the examples below the body of custom-cmd
is (^cmd1 | ^cmd2; ^cmd3 | ^cmd4)
.
- custom-cmd
Command | Stdout | Stderr |
---|---|---|
cmd1 | Piped | Terminal |
cmd2 | Terminal | Terminal |
cmd3 | Piped | Terminal |
cmd4 | Terminal | Terminal |
- custom-cmd | ^cmd5
It runs custom-cmd
first, then pipes stdout to ^cmd5
, where both stdout and stderr are directed to the Terminal.
Command | Stdout | Stderr |
---|---|---|
cmd1 | Piped | Terminal |
cmd2 | Terminal | Terminal |
cmd3 | Piped | Terminal |
cmd4 | Piped | Terminal |
- custom-cmd e>| ^cmd5
It runs custom-cmd
first, then pipes stderr to ^cmd5
, where both stdout and stderr are directed to the Terminal.
Command | Stdout | Stderr |
---|---|---|
cmd1 | Piped | Terminal |
cmd2 | Terminal | Terminal |
cmd3 | Piped | Terminal |
cmd4 | Terminal | Piped |
- custom-cmd o+e>| ^cmd5
It runs custom-cmd
first, then pipes stdout and stderr to ^cmd5
, where both stdout and stderr are directed to the Terminal.
Command | Stdout | Stderr |
---|---|---|
cmd1 | Piped | Terminal |
cmd2 | Terminal | Terminal |
cmd3 | Piped | Terminal |
cmd4 | Piped | Piped |
- custom-cmd o> test.out
Command | Stdout | Stderr |
---|---|---|
cmd1 | Piped | Terminal |
cmd2 | File | Terminal |
cmd3 | Piped | Terminal |
cmd4 | File | Terminal |
- custom-cmd e> test.out
Command | Stdout | Stderr |
---|---|---|
cmd1 | Piped | File |
cmd2 | Terminal | File |
cmd3 | Piped | File |
cmd4 | Terminal | File |
- custom-cmd o+e> test.out
Command | Stdout | Stderr |
---|---|---|
cmd1 | Piped | File |
cmd2 | File | File |
cmd3 | Piped | File |
cmd4 | File | File |