0

When running Ripgrep normally in Bash, the output is formatted as follows:

$ rg asdfghjkl
my_file
1:asdfghjkl

But if I attempt to capture the output with $(), the format is different:

$ foo=$(rg asdfghjkl)
$ echo "$foo"
my_file:asdfghjkl

What causes this disparity? Is Ripgrep changing its behaviour, or does $() not really capture stdout?

My best guess is that Ripgrep is the issue rather than Bash, though I've not been able to find anything documenting this behaviour.

Alira
  • 55
  • 1
  • 6

2 Answers2

4

It is ripgrep. You can see the same behavior by other commands. E.g ls prints entries in columns ls | cat prints them one per line. grep something will usually colorize the match but with grep | cat the color is gone.

Commands can and do test if the standard output is connected to an interactive terminal (your first example) or not (your second example which is basically the same as a pipeline).

Same exist for standard input where a command could check if it is connected to a terminal to decide if it should show a password prompt or not.

Usually the output a command produces for the pipeline case is more suitable for parsing and using in scripts. And you might check if ripgrep has options to force a specific output format.

Paul Pazderski
  • 473
  • 2
  • 8
  • I don't follow what's happening in your `ls | cat` example. Is this `ls` or `cat` behaviour you're talking about? `foo=$(ls); echo foo` doesn't have one entry per line. – Alira Jul 25 '23 at 22:50
  • @Alira it's `ls` behavior. As far as `ls` is concerned, `foo=$(ls)` is the same as `ls` and that's different from `ls | cat`. – Ed Morton Jul 25 '23 at 23:02
  • 1
    "foo=$(ls); echo foo doesn't have one entry per line." And `ls` alone has. `ls` is the interesting part, `cat` is boring. It doesn't matter if you try `command | cat` or `echo "$(command)"` it is almost the same. In the first one command output is redirected to `cat` which does nothing more than echoing anything it get from stdin. With your `$(ls)` also the output is redirected to store it in `foo`. So for `ls` both are the same. Output is redirected therefore `ls` changes behavior. `command | cat` is just the simplest test for this because `cat` is so simple. – Paul Pazderski Jul 25 '23 at 23:07
  • @PaulPazderski So if `ls` doesn't behave differently in `$()`, am I right in concluding the concept of "being connected to an interactive terminal" is not standardised between processes? If so, what other conditions are typical for processes to consider themselves in that state? – Alira Jul 26 '23 at 18:01
  • 1
    `ls` **does** behave differently in `$()`. Because in `$()` the output is kinda connected with your variable instead of the terminal. A process which want's to know if it is connected to a terminal or not uses a system call or other helper function to tell it. For example in bash the test `[[ -t 1 ]]` (stdout is always file descriptor 1) checks if stdout is connected to a terminal or not. [Here](https://unix.stackexchange.com/a/515781/573555) are more details on that. So the concept of checking if connected to terminal or not is kinda standardized. – Paul Pazderski Jul 26 '23 at 18:12
3

When ripgrep detects that output does not go to the terminal, it suppresses the heading. If you still want the heading, then explicitly specify it:

$ foo=$(rg asdfghjkl --heading)
Hai Vu
  • 37,849
  • 11
  • 66
  • 93