0

Consider the following zsh script:

echo $(echo "yes")

Now, echo takes a string and turns it into standard output. On the other hand, the $(X) notation evaluates X and turns its standard output into a string.

Expectedly, the standard output is as follows:

 dteiml@DominiksMBP2019 ~ echo $(echo "yes")
yes

So far so good.

Now consider the following script involving bat. bat is a pretty-prettier for cat, kind of like Pygments.

echo "yes" | bat

The standard output is as follows:

 dteiml@DominiksMBP2019 ~ echo "yes" | bat
───────┬─────────────────────────────────
       │ STDIN
───────┼─────────────────────────────────
   1   │ yes
───────┴─────────────────────────────────

So I would expect the following command:

echo $(echo "yes" | bat)

to produce the same output (pretty-formatted). However, it just outputs "yes":

dteiml@DominiksMBP2019 ~ echo $(echo "yes" | bat)
yes

Based on the above, this is unexpected. Does it use some zsh api that would allow it to determine if it's being executed inside a $(X) block, or something else?

PS I don't really need this for anything, just trying to get a better understanding of bash/zsh.

Thanks in advance!

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
Dominik Teiml
  • 505
  • 1
  • 4
  • 12
  • `bat` is checking whether its output is a TTY and declining to reformat for human-targeted output when the output is a FIFO instead of a TTY. This isn't a bash/zsh choice, it's a bat choice. – Charles Duffy Sep 07 '22 at 17:42
  • That said, if your shell were bash instead of zsh and bat _did_ create the table in question (instead of doing that only if `isatty(1)` is true), you'd have some serious bugs unless you changed your code to `echo "$(echo "yes" | bat)"` _with the quotes_. – Charles Duffy Sep 07 '22 at 17:43
  • (Please only tag one or the other of bash and zsh, not both at the same time; they're not mutually compatible) – Charles Duffy Sep 07 '22 at 17:43
  • (this is the same as how `ls --color=auto` only writes color-formatting when going straight to a terminal, not when writing to a pipe). – Charles Duffy Sep 07 '22 at 17:45
  • ...for where `bat` performs this check, see https://github.com/sharkdp/bat/blob/08c91a116c7513518e857c5e3b9d629e81d98f80/src/bin/bat/app.rs#L163-L180, using a variable populated at https://github.com/sharkdp/bat/blob/08c91a116c7513518e857c5e3b9d629e81d98f80/src/bin/bat/app.rs#L44 -- `atty::is(Stream::Stdout)` – Charles Duffy Sep 07 '22 at 17:49
  • ...and the library it calls into is invoking `libc::isatty(fd)` at https://docs.rs/atty/0.2.14/src/atty/lib.rs.html#48 – Charles Duffy Sep 07 '22 at 17:50
  • ...and just to emphasize something that may have been implicit above: `isatty()` isn't communicating with the calling shell: it still works if there _is_ no copy of bash or zsh or any other shell running as a parent process; rather, it's communicating with the operating system kernel to ask if stdout is pointing to a TTY object (`/dev/pty*` etc) as opposed to any other kind of file handle. (Or, rather, it's using the libc, which is responsible for knowing how the local OS communicates that, whether it requires a syscall or not). – Charles Duffy Sep 07 '22 at 17:52

0 Answers0