Nushell
Get Nu!
Getting Started
  • The Nushell Book
  • Command Reference
  • Cookbook
  • Language Reference Guide
  • Contributing Guide
Blog
  • English
  • 中文
  • Deutsch
  • Français
  • Español
  • 日本語
  • Português do Brasil
  • Русский язык
  • 한국어
GitHub
Get Nu!
Getting Started
  • The Nushell Book
  • Command Reference
  • Cookbook
  • Language Reference Guide
  • Contributing Guide
Blog
  • English
  • 中文
  • Deutsch
  • Français
  • Español
  • 日本語
  • Português do Brasil
  • Русский язык
  • 한국어
GitHub
  • Language Reference Guide
    • Readme
    • Types in the Nu Language
      • Basic Types
        • Any
        • Boolean
        • Integer
        • Float
        • Filesize
        • Duration
        • Datetime
        • Range
        • String
        • Record
        • List
        • Table
        • Closure
        • Nothing
        • Binary
        • Glob
        • Cell-Path
      • Other Data Types

        • Types used only in command signatures
          • Path
        • Types which are not declarable
          • Error
          • CustomValue
          • Block
      • Type signatures
      • Commands that interact with types
    • Operators
    • Flow control
      • if/else
      • loop
      • while
      • match
      • try/catch
      • break
      • return
      • continue
    • Filters
      • each and par-each
      • Filters to select subsets of data
      • where and filter
      • Understanding the difference between get and select
    • Custom Commands
    • Declarations
    • Variable Scope
    • Strings and Text Formatting
    • Helpers and debugging commands
    • Pipelines
    • MIME Types for Nushell

Pipelines

The pipeline special variable $in

$in holds the input of the current pipeline stage. It can be used in an expression that is itself a stage of the pipeline, in a closure, or in the body of a custom command:

[3 1 2] | sort | $in.0
# => 1
"hello" | $"($in) world"
# => hello world

See Pipeline Input and the Special $in Variable in the Book for details.

Best practices for pipeline commands

Interaction with Unix pipes

The output of an external command is a stream of bytes, which Nushell passes on to the next command as it arrives. If any external command in a pipeline exits with a non-zero exit code, the pipeline fails, even when it is not the last command (like set -o pipefail in Bash). Use try to handle the failure, or complete to capture the exit code:

try { ^false | lines } catch {|e| $e.exit_code }
# => 1

Handling stdout and stderr

You can handle stderr in multiple ways:

  1. Do nothing, stderr will be printed directly
  2. Pipe stderr to the next command, using e>| or o+e>|
  3. Redirect stderr to a file, using e> file_path, or o+e> file_path
  4. 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:

typecommand$result contentsprinted to terminal
|let result = nu demo.nu | str uppercase"FOO""barbar"
e>|let result = nu demo.nu e>| str uppercase"BARBAR""foo"
o+e>|let result = nu demo.nu o+e>| str uppercase"FOO\nBARBAR"nothing

Redirection to a file:

typecommandfile.txt contentsprinted to terminal
o> file_pathnu demo.nu o> file.txt"foo\n""barbar"
e> file_pathnu demo.nu e> file.txt"barbar\n""foo"
o+e> file_pathnu demo.nu o+e> file.txt"foo\nbarbar\n"nothing

complete command:

typecommand$result contents
use completelet result = do { nu demo.nu } | completerecord 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: nu::shell::error
# =>
# =>   × Can't redirect stderr of internal command output
# =>    ╭─[repl_entry #1:1:4]
# =>  1 │ ls e>| str length
# =>    ·    ─┬─
# =>    ·     ╰── piping stderr only works on external commands
# =>    ╰────

ls o+e>| str length
# => Error: nu::shell::error
# =>
# =>   × Can't redirect stderr of internal command output
# =>    ╭─[repl_entry #2:1:4]
# =>  1 │ ls o+e>| str length
# =>    ·    ──┬──
# =>    ·      ╰── piping stderr only works on external commands
# =>    ╰────

You can also redirect stdout to a file, and pipe stderr to next command:

nu demo.nu o> file.txt e>| str uppercase
# => BARBAR
nu demo.nu e> file.txt | str uppercase
# => FOO

But you can't use redirection along with o+e>|, because it's ambiguous:

nu demo.nu o> file.txt o+e>| str uppercase
# => Error: nu::parser::multiple_redirections
# =>
# =>   × Multiple redirections provided for stdout.
# =>    ╭─[repl_entry #1:1:12]
# =>  1 │ nu demo.nu o> file.txt o+e>| str uppercase
# =>    ·            ─┬          ──┬──
# =>    ·             │            ╰── second redirection
# =>    ·             ╰── first redirection
# =>    ╰────

Also note that complete treats whatever is piped into it as the command's stdout. With e>|, the stdout field of the result holds the command's stderr (and stderr holds its stdout). With o+e>|, both streams end up in stdout. Use a plain | to get the two streams in their own fields.

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)
CommandStdoutStderr
cmd1PipedTerminal
cmd2TerminalTerminal
cmd3PipedTerminal
cmd4TerminalTerminal
  • (^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.

CommandStdoutStderr
cmd1PipedTerminal
cmd2TerminalTerminal
cmd3PipedTerminal
cmd4PipedTerminal
  • (^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.

CommandStdoutStderr
cmd1PipedTerminal
cmd2TerminalTerminal
cmd3PipedTerminal
cmd4TerminalPiped
  • (^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.

CommandStdoutStderr
cmd1PipedTerminal
cmd2TerminalTerminal
cmd3PipedTerminal
cmd4PipedPiped
  • (^cmd1 | ^cmd2; ^cmd3 | ^cmd4) o> test.out
CommandStdoutStderr
cmd1PipedTerminal
cmd2FileTerminal
cmd3PipedTerminal
cmd4FileTerminal
  • (^cmd1 | ^cmd2; ^cmd3 | ^cmd4) e> test.out
CommandStdoutStderr
cmd1PipedFile
cmd2TerminalFile
cmd3PipedFile
cmd4TerminalFile
  • (^cmd1 | ^cmd2; ^cmd3 | ^cmd4) o+e> test.out
CommandStdoutStderr
cmd1PipedFile
cmd2FileFile
cmd3PipedFile
cmd4FileFile

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
CommandStdoutStderr
cmd1PipedTerminal
cmd2TerminalTerminal
cmd3PipedTerminal
cmd4TerminalTerminal
  • custom-cmd | ^cmd5

It runs custom-cmd first, then pipes stdout to ^cmd5, where both stdout and stderr are directed to the Terminal.

CommandStdoutStderr
cmd1PipedTerminal
cmd2TerminalTerminal
cmd3PipedTerminal
cmd4PipedTerminal
  • custom-cmd e>| ^cmd5

It runs custom-cmd first, then pipes stderr to ^cmd5, where both stdout and stderr are directed to the Terminal.

CommandStdoutStderr
cmd1PipedTerminal
cmd2TerminalTerminal
cmd3PipedTerminal
cmd4TerminalPiped
  • 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.

CommandStdoutStderr
cmd1PipedTerminal
cmd2TerminalTerminal
cmd3PipedTerminal
cmd4PipedPiped
  • custom-cmd o> test.out
CommandStdoutStderr
cmd1PipedTerminal
cmd2FileTerminal
cmd3PipedTerminal
cmd4FileTerminal
  • custom-cmd e> test.out
CommandStdoutStderr
cmd1PipedFile
cmd2TerminalFile
cmd3PipedFile
cmd4TerminalFile
  • custom-cmd o+e> test.out
CommandStdoutStderr
cmd1PipedFile
cmd2FileFile
cmd3PipedFile
cmd4FileFile
Edit this page on GitHub
Contributors: NotTheDr01ds, Wind, fdncred, Jan Klass, kkoang, 132ikl
Prev
Helpers and debugging commands
Next
MIME Types for Nushell