4

Question

What are the semantics of : in regards to its use in pipes?

The Bash documentation states:

: [arguments]
No effect; the command does nothing beyond expanding arguments and performing any specified redirections. The return status is zero.

However, it's not obvious what behavior I ought to expect from : being used in a pipe. Does it simply pass through std{in,out,err}?

In regards to the arguments : can be a stand-in for any other command, it seems. But I need a clarification regarding the use in pipe; a clarification not merely based upon observation, but citing authoritative sources.

Background

I have a situation, where -- depending on the environment the script runs in --, I need to run the output through tr -d '\r' to remove carriage return characters from the output.

Alas, currently this means an if/else/fi block, along the lines of (inside a function):

if [[ "$OSTYPE" == "cygwin" ]]; then
    my_commands | tr -d '\r' || return $?
else
    my_commands || return $?
fi

Where the | tr -d '\r' is the only difference between the if and the else branch. The contents of either branch are of course quite a bit more complex than shown here.

Now, I get it that the tr -d '\r' is benign in cases where there is no carriage return. But it's an invocation and (on Windows) an overhead that could be avoided (the function is a "hot" code path). And so I thought of replacing tr by the builtin shell function :.

3
  • 1
    (very) related: Conditional pipeline Commented Jun 2, 2023 at 10:35
  • 5
    Beware that unless you enable the pipefail option, the || return "$?" will only check the exit status of tr (itself unlikely to fail). Commented Jun 2, 2023 at 10:47
  • FYI, you can pipe to if, which may reduce some duplication: command_a | command_b | if [[ ... ]]; then command_c | tr -d' '\r'; else command_c; fi || return $?
    – Vaelus
    Commented Jun 3, 2023 at 18:11

3 Answers 3

14

The pass-through replacement in this case is cat, which copies its input to its output. : does nothing so your input would be lost:

$ echo hello | :
$

In fact, since : doesn’t read its input, commands feeding the pipe will receive a SIGPIPE, so they might not finish their task, or even start it:

$ (seq 1 10000 | tee /dev/stderr; echo $? 1>&2) | :
141

Using cat means an external invocation, just like tr (but with slightly less work). Bash doesn’t have a pass-through built-in of this kind.

6
  • Thanks Stephen for this concise, yet comprehensive answer. Shatters my hopes, but helps a lot ;) Commented Jun 2, 2023 at 8:48
  • I guess the doubt arose because in the context of filters what cat does is a no-op and what : does is "delete everything". Commented Jun 2, 2023 at 8:50
  • 5
    @KamilMaciorowski I would call cat an "identity filter" rather than a "no-op".
    – Kusalananda
    Commented Jun 2, 2023 at 8:51
  • 1
    @KamilMaciorowski not quite, : doesn’t delete everything, it just doesn’t read anything — if the feeder command produces more output than the pipe buffer holds, it will get a SIGPIPE. Commented Jun 2, 2023 at 9:35
  • 1
    Note also that, if you're one of those people who sets pipefail, then piping to : will count as a failed command, because the writer will probably die of SIGPIPE rather than exiting normally. This is why redirection to /dev/null is probably safer in most cases (and why pipefail should be used with care).
    – Kevin
    Commented Jun 2, 2023 at 21:26
10

Regardless of the odd name, : is still a command and acts like one, so when it doesn't do anything, it means it also doesn't read anything from the pipe. It's not special syntax for the shell in the sense that | : | would reduce back to just |. The POSIX description for : does say it doesn't "use" stdin or stdout, but of course it doesn't explicitly mention pipelines there.

There are some solutions to this in Conditional pipeline, but mostly it's hard or awkward to avoid running a copy of cat in the middle.

(fine print: The POSIX text uses the term "utility", but it's still the same term used for e.g. ls. Also, : isn't exactly a regular command, but a "special built-in", which makes a difference in some minor cases, but not here.)

1
  • The only "use" that I have found for : is when I am building a script. Functions and if-then structures, etc. need something to execute. Suppose I've planned out the idea and what functions there will be. But I haven't worked out the code yet, and the : serves as a placeholder so the rest of the script runs without an error such as "syntax error near unexpected token `}'" . Then I can move on. I think it is a programming tool more than anything else.
    – Wastrel
    Commented Jun 16, 2023 at 23:36
3

The key phrase in the man page is that : (like its synonym true) does nothing. It doesn't read anything from its input, nor does it write anything to its output streams.

In a pipe, it's not very useful - it acts like a blockage in the plumbing.

0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .