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 containsTODOxargs 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.