bat
bat copied to clipboard
possibility to suppress output if stdin is empty and set header manually
I'm using sed
commandline to fetch all of TODO
in source code and shows via batcat
as below:
$ fd -tf --color never |
xargs -r -I{} bash -c "sed -ne '/TODO:/,/^\s*$/p' {} | bat -l groovy"
However, the bat
shows STDIN <EMPTY>
if sed
command returns nothing.
currently I'm using if..else..
to show content via bat as below:
$ while read -r file; do
_content=$(sed -ne '/TODO:/,/^\s*$/p' "${file}");
[[ -n "${_content}" ]] && echo "${_content}" | bat -l groovy;
done < <(fd -tf --color never)
Any better solution to suppress output if stdin is empty, and by the way, it's nice to have a feature to setup header manually if content is from stdin to easier identify where the output comes from
thanks.
I think having an extra opt-in command line flag to disable printing the header for binary and/or empty files would be fine. It would be especially useful when using bat
in bash scripts, like with what you're doing here.
It's nice to have a feature to setup header manually if content is from stdin to easier identify where the output comes from
The --file-name
option may be helpful here. It's even used to detect the language type :)
For your specific example of finding TODOs in source code files, ripgrep might be a more performant approach compared to fd
+sed
:
rg --vimgrep --with-filename 'TODO' | cut -d':' -f1 | uniq | xargs bat
There's also batgrep if you want a bit more of a tailored experience:
Hi @eth-p ,
for me, it's just show ALL contents of files contains TODO
, for my understanding:
-
rg --vimgrep --with-filename 'TODO' | cut -d':' -f1 | uniq
: list all filepath containsTODO
-
xargs bat
: bat all contents
using sed -ne '/TODO:/,/^\s*$/p'
, means print lines between TODO
line to first empty line ^\s*$
, since almost of all TODO
is added before function, so using sed
will show whole function body
base on your example, following commands resolved STDIN <EMPTY>
issue:
rg --vimgrep --with-filename 'TODO' --color never |
cut -d':' -f1 |
uniq |
xargs -r -I{} bash -c "sed -ne '/TODO:/,/^\s*$/p' {} |
bat -l groovy"
However, it still didn't show the exact file name... i.e.:
here is my batconfig
$ cat ~/.config/bat/config | sed -r '/^(#.*)$/d' | sed -r '/^\s*$/d'
--theme="gruvbox-dark"
--style="numbers,changes,header"
--italic-text=always
--pager="less --RAW-CONTROL-CHARS --quit-if-one-screen --mouse"
--map-syntax "*.ino:C++"
--map-syntax ".ignore:Git Ignore"
--map-syntax='*.conf:INI'
--map-syntax='/etc/apache2/**/*.conf:Apache Conf'
--map-syntax "**/jenkinsfile/**/*:Groovy"
more on sed && sed+bat:
Hi @marslo
Your understanding of both of those is correct, yes.
For your issue with bat
displaying the file name as "STDIN", you can use the --file-name
argument in bat
to have it change the name in the header. Let me know if the modified code below works for you:
rg --vimgrep --with-filename 'TODO' --color never |
cut -d':' -f1 |
uniq |
xargs -r -I{} bash -c "sed -ne '/TODO:/,/^\s*$/p' {} |
bat -l groovy --file-name='{}'"
ops... forgot to let you know, your solution works like charm, you can close this issue. thanks again.
btw to show the correct line number in the file, it can be handled by nl {}
and without -l groovy
to let bat automatic use:
rg --vimgrep --with-filename 'TODO:' --color never |
cut -d':' -f1 |
uniq |
xargs -r -I{} bash -c "sed -ne '/TODO:/,/^\s*$/p' < <(nl {}) |
bat --style="grid,changes,header" --file-name='{}'"
Since we haven lot of files/dotfiles has not extension, so, to identify the language automatically, I'm using the following function ( a little bit ugly, but I don't how to handle via complex shell logic in xargs
, so using while read
instead of ):
function showTODO() {
local option='--style="grid,changes,header"'
while [[ $# -gt 0 ]]; do
case "$1" in
-p | --plain ) option+=" $1" ; shift 1 ;;
-* ) option+=" $1 $2" ; shift 2 ;;
esac
done
rg --vimgrep --with-filename 'TODO:' --color never |
cut -d':' -f1 |
uniq |
while read -r _file; do
# identify language automatically
local lang='';
lang="$(sed -r 's/^.+\.(.+)$/\1/' <<< "${_file}")";
if ! bat --list-languages | command grep -iE -q "[,:]${lang}|${lang},"; then
# check shebang and reset to empty if shebang not found
lang=$(sed -rn 's/^#!.*[/\ ](\w+)$/\1/p' < <(head -n1 "${_file}"));
fi
sed -ne '/TODO:/,/^\s*$/p' < <(nl "${_file}") |
eval "bat -l ${lang:-groovy} ${option} --file-name=\"${_file}\"" ;
done
if bat can detect filetype via shebang
line ( i.e.: #!/usr/bin/env bash
or #!/bin/bash
, via ^#!.*[/\ ](\w+)$
), then the simple version will be the best answer.
btw, just curious why bat
will using _fzf_path_completion
as completion function by default.