`--style` documentation is misleading
The description on --style is misleading. It says "auto: same as 'full', unless the output is piped." and "full: enables all available components (default)".
This is misleading - I read it as saying that when --style=full is passed, the full decorations are enabled in a command like this: echo '' | bat --style=full | cat. This is not the case - this pipeline prints a newline rather than something like "STDIN\n 1 ". Compare the output with echo '' | bat --style=full. If the output is piped, loop_through is enabled, the SimplePrinter is selected, and the output is printed without any decorations regardless of the --style=full flag. This effectively throws away the style configuration whenever cat-compatibility mode is enabled, which is not what the documentation says.
As a secondary question, what is the point of the --style=auto option if the style is ignored when the output is piped? It provides the same functionality as --style=full --decorations=auto. It seems like having both is redundant.
Seems like this is the same issue I came to report, please let me know if I should make a separate one, but when piping with the -n flag (equivalent to —style numbers) I note that no numbers are passed.
(I think this is less a documentation issue and should rather affect the behaviour to be clear!)
I believe https://github.com/sharkdp/bat/issues/2935 is related
i've also recently encountered this issue of style options (--style=header-filename in my case) not being passed when piping to another program. while asking an LLM for workarounds it informed me that setting --color=always will preserve style options when piped - which works regardless of whether coloring is part of the style, apparently
Bat vivisection
Looked into it so as to be more precise about what we are discussing:
Click to show code walkthrough
-
src/controller.rshas the control over the printer that gets used, detecting based on whether it's being piped or not
https://github.com/sharkdp/bat/blob/a730eaae0a17de2c2fd8099471a49e7419db1b54/src/controller.rs#L179-L189
Though I note that we do actually have the config here so perhaps we could either
- Use it with the SimplePrinter (I suspect so)
- Switch to the InteractivePrinter based on the presence of style in the
style_componentsfield of theself.config(config::Configstruct)
https://github.com/sharkdp/bat/blob/a730eaae0a17de2c2fd8099471a49e7419db1b54/src/config.rs#L66-L67
It then calls print file
https://github.com/sharkdp/bat/blob/a730eaae0a17de2c2fd8099471a49e7419db1b54/src/controller.rs#L191-L199
Which will call
self.print_file_ranges(printer, writer, &mut input.reader, &line_ranges)?;
https://github.com/sharkdp/bat/blob/a730eaae0a17de2c2fd8099471a49e7419db1b54/src/controller.rs#L201-L237
which is calling the print_line method on whichever Printer[-trait implenting struct] we have, in a loop:
https://github.com/sharkdp/bat/blob/a730eaae0a17de2c2fd8099471a49e7419db1b54/src/controller.rs#L298-L321
(Which means either the SimplePrinter or the InteractivePrinter)
The SimplePrinter writes to the handle (presumably STDOUT) all the time it's in the line range, but doesn't refer to the style components config field at all.
https://github.com/sharkdp/bat/blob/a730eaae0a17de2c2fd8099471a49e7419db1b54/src/printer.rs#L156-L168
The InteractivePrinter ...has a very long equivalent routine
https://github.com/sharkdp/bat/blob/a730eaae0a17de2c2fd8099471a49e7419db1b54/src/printer.rs#L604-L857
You can see the stye_components field crop up when it pushes a LineNumberDecoration onto a list of decorations
https://github.com/sharkdp/bat/blob/a730eaae0a17de2c2fd8099471a49e7419db1b54/src/printer.rs#L224-L226
My initial impression is that if this were to change it may surprise people if regular piping suddenly started decorating style.
But in my case I want it to, at least when I actively pass in a style like -n. Not sure if it'd be possible to distinguish someone passing in the default style vs. it being defaulted to.
Maybe we need:
- a 3rd printer, neither Simple nor Interactive, but de-coloured?
- an extra config flag, to record if the user explicitly set a flag?
- As an initial approximation we could simply set this based on the presence of non-default/non-empty style components
- ...in which case it doesn't need to be a new struct field at all
- As an initial approximation we could simply set this based on the presence of non-default/non-empty style components
- ❇️ something else... ❇️
Proposed solution?
If it'd be OK to gate on just plain(), then adding at line 179 in controller.rs:
&& self.config.style_components.plain() {
you'd get
louis 🌟 ~/dev/bat $ echo "Hello" | ./target/debug/bat | cat
─────┬──────────────────────────────────────────────────────────────────────────
│ STDIN
─────┼──────────────────────────────────────────────────────────────────────────
1 │ Hello
─────┴──────────────────────────────────────────────────────────────────────────
louis 🌟 ~/dev/bat $ echo "Hello" | ./target/debug/bat -p | cat
Hello
and
louis 🌟 ~/dev/bat $ echo "Hello" | ./target/debug/bat -n | cat
1 Hello
louis 🌟 ~/dev/bat $ ./target/debug/bat Cargo.toml -r :4 | cat
─────┬──────────────────────────────────────────────────────────────────────────
│ File: Cargo.toml
─────┼──────────────────────────────────────────────────────────────────────────
1 │ [package]
2 │ authors = ["David Peter <[email protected]>"]
3 │ categories = ["command-line-utilities"]
4 │ description = "A cat(1) clone with wings."
─────┴──────────────────────────────────────────────────────────────────────────
I should add that the colour is handled appropriately:
You can pipe to clipboard and not get ANSI codes in the string 🥳
Ah! I've just found there was an abandoned PR with the same discussion!
- #2983
From the comment there it sounds like it in fact is the preference to retain the behaviour of plain as default when piping. I'd really like to be able to copy what I see on my terminal to clipboard, so I'd be pro-switching from "plain as default when piped" to "default style as default when piped". I guess it comes down to whether people have workflows/habits that depend on piping plainly
The penultimate comment on that PR seems to suggest that -n should be piped through
I think I've found a simpler approach. The following should do the trick:
diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs index 8f69870f..cf385c88 100644 --- a/src/bin/bat/app.rs +++ b/src/bin/bat/app.rs @@ -251,9 +251,10 @@ impl App { loop_through: !(self.interactive_output || self.matches.get_one::<String>("color").map(|s| s.as_str()) == Some("always") || self .matches .get_one::<String>("decorations") .map(|s| s.as_str()) == Some("always") - || self.matches.get_flag("force-colorization")), + || self.matches.get_flag("force-colorization") + || self.matches.get_flag("number")), tab_width: selfThat way, whenever
-n/--numberis specified the defaultInteractivePrinteris used instead of theSimplePrinter.
but that's not what's in the code at present
Moreover I think they have omitted the use of the --style flag which is meant to behave equivalently
You can basically see if someone has set the style to numbers by checking for the flag's presence AND the config being set for numbers, i.e. also this:
|| (self.matches.contains_id("style") && style_components.numbers())
In fact though that produces a bug where you end up piping the full style options (header, grid) only if you have numbers in that CSV style! So obviously what you actually want is to make all styles pipeable if set explicitly via --style (which would therefore leave the default behaviour alone)
You'd want to make an exception for when the style is set as plain (that should continue to use the simple printer)
|| (self.matches.contains_id("style") && !style_components.plain())
My interpretation of the docs here is that --style=default means it should pipe, and --style=auto should not
* default: enables recommended style components (default).
* full: enables all available components.
* auto: same as 'default', unless the output is piped.
So there'd also be a switch on the style not being set to auto (but default would be OK)
It turns out that condition is actually sufficient, so auto must already set the style config to plain, it "just works"!
i've also recently encountered this issue of style options (
--style=header-filenamein my case) not being passed when piping to another program. while asking an LLM for workarounds it informed me that setting--color=alwayswill preserve style options when piped - which works regardless of whether coloring is part of the style, apparently
Now you no longer need the --color=always :-)
