Skip to main content

nu_command/platform/
is_redirected.rs

1//! Nushell pipeline-destination check for custom commands.
2//!
3//! Complements [`super::is_terminal`]: `is-terminal` is OS `isatty`, while this command
4//! reports if a custom command's *return value* is piped, collected, filed, or
5//! discarded rather than printed.
6//!
7//! Implementation relies on [`Stack::is_stdout_redirected`], which reads the
8//! invocation-stdout frame pushed in `eval_call` via [`Stack::with_invocation_stdout`].
9//!
10//! Note: help completion matches substrings of description / extra_description, so
11//! avoid accidental tokens like "who" inside common words ("whether", "whole").
12
13use nu_engine::command_prelude::*;
14
15#[derive(Clone)]
16pub struct IsRedirected;
17
18impl Command for IsRedirected {
19    fn name(&self) -> &str {
20        "is-redirected"
21    }
22
23    fn signature(&self) -> Signature {
24        Signature::build("is-redirected")
25            .input_output_type(Type::Nothing, Type::Bool)
26            .category(Category::Platform)
27    }
28
29    fn description(&self) -> &str {
30        "Check if the current custom command's return value is redirected away from display."
31    }
32
33    fn extra_description(&self) -> &str {
34        "This is a Nushell pipeline-destination check, not an OS TTY check.
35
36Inside a custom command, `is-redirected` reports if that command's return
37value will be piped to another command, collected into a value (`let`,
38subexpression), written to a file, or discarded — as opposed to being printed
39via the normal display path.
40
41Unlike looking at process stdout, this is stable for the entire command body, so
42it works inside `if (...)` and `let x = (...)`.
43
44To test if process stdout is a terminal (scripts: `./script | cat`), use
45`is-terminal --stdout` instead.
46
47Typical pattern for pretty-vs-data custom commands:
48
49    def mycmd [] {
50      if (is-terminal --stdout) and not (is-redirected) {
51        # human-friendly formatting
52      } else {
53        # structured data for pipelines
54      }
55    }
56"
57    }
58
59    fn examples(&self) -> Vec<Example<'_>> {
60        vec![
61            Example {
62                description: "Inside a custom command, report if the call's return value is redirected (works in `if`).",
63                example: r#"def pipetest [] { if (is-redirected) { "piped" } else { "display" } }; pipetest"#,
64                // Display vs redirect depends on how the call is invoked; result omitted.
65                result: None,
66            },
67            Example {
68                description: "Return true when the custom command is piped to another command.",
69                example: "def pipetest [] { is-redirected }; pipetest | $in",
70                result: Some(Value::test_bool(true)),
71            },
72            Example {
73                description: "Return true when the custom command result is collected into a variable.",
74                example: "def pipetest [] { is-redirected }; let x = (pipetest); $x",
75                result: Some(Value::test_bool(true)),
76            },
77        ]
78    }
79
80    fn search_terms(&self) -> Vec<&str> {
81        // Must not be substrings of the command name (see command_context tests).
82        vec!["pipe", "pipeline", "display", "stdout", "tty"]
83    }
84
85    fn run(
86        &self,
87        _engine_state: &EngineState,
88        stack: &mut Stack,
89        call: &Call,
90        _input: PipelineData,
91    ) -> Result<PipelineData, ShellError> {
92        Ok(PipelineData::value(
93            Value::bool(stack.is_stdout_redirected(), call.head),
94            None,
95        ))
96    }
97}