Bat: zcat like functionality

Created on 30 Aug 2018  路  15Comments  路  Source: sharkdp/bat

It would be great if bat would detect if a file or stream is gzipped and would display it like zcat does.

feature-request question

Most helpful comment

Thanks for fixing #891. I think the following works, in case anyone else wants to pick it up:

#!/bin/sh
set -euo pipefail

case "$#" in
    1)
        mode=list
        fname='<unset>'
        ;;
    2)
        mode=extract
        fname="$2"
        ;;
    *)
        echo "Usage: $0 archive-name [file-name]" >&2
        exit
        ;;
esac
aname="$1"
atype="$(file -bi "$aname" | cut -d\; -f1)"
ztype="$(file -Zbi "$aname" | cut -d\; -f1)"
if [[ "$ztype" = application/x-tar ]]; then
    listcmd=(tar --list --file "$aname")
    extractcmd=(tar --extract --to-stdout --file "$aname" "$fname")
elif [[ "$atype" = application/zip ]]; then
    listcmd=(unzip -l "$aname")
    extractcmd=(unzip -p "$aname" "$fname")
else
    echo "Unknown filetype: $atype" >&2
    exit 1
fi
case "$mode" in
    list)
        "${listcmd[@]}" | bat
        ;;
    extract)
        "${extractcmd[@]}" | bat --file-name "$fname"
        ;;
esac

All 15 comments

Thank you for the feedback!

I'm inclined to say no. I see it as a niche feature that would be nice-to-have but I'm not sure if its worth the effort (to maintain this feature in the future) given that we can easily compose gzip and bat via UNIX pipes.

That being said, if someone wants to work on this feature and it turns out that this can be implemented rather easily (i.e. it doesn't significantly increase code complexity), I think it could be a good fit for bat.

I'm going to close this. If there is any further feedback, feel free to comment and I can re-open this ticket.

It's a bit more tricky than just "composing gzip and bat": one also needs to pass the correct language to bat to get correct highlighting, e.g. unzip -p foo.zip blah.rs | bat -l rs -- and the correct extension to pass isn't necessarily obtained by just keeping everything after the last dot (e.g. .bash_profile, Makefile.in, etc.).
I guess it would be nice if e.g. bat -l (or an orthogonal option) also supported passing in the full filename, in which case it would be relatively easy to write a wrapper, e.g. (untested)

function zbat() { unzip -p "$1" "$2" | bat -l "$2" }

I guess it would be nice if e.g. bat -l (or an orthogonal option) also supported passing in the full filename

Interesting idea! Could you please open a new ticket for this feature request?

I was going to, and then github's related issues window showed me https://github.com/sharkdp/bat/issues/654, which I guess is basically the same thing. So I guess I'll just :+1: #654 :)

Hm, I guess you are right. This is related. I was thinking about something slightly different. There are also files where we need to determine the syntax from the (first line) content.

What if someone runs

echo '#!/bin/env python\nprint("hi")' | bat --override-file-name test.cpp

Should it be highlighted as Python or C++?

I don't know, what does bat do if there's a file (real file on the disk) with conflicting contents and name? Doing the same thing seems fine.
I guess you could have separate --use-filename-but-also-read-contents and --use-filename-and-ignore-contents (name obviously just for demo purposes), but that seems overkill.

Edit: I see #654 is more on the force the filename side, whereas I would rather keep whatever bat currently does when the two sources of info are conflicting.

Agreed :+1:

@sharkdp #654 just went in, but it only sets the displayed name whereas for this feature I would also like the displayed name to be used to detect the syntax highlighting to be used.
Do you think this is something that should be folded into --file-name? Or should I open a separate issue to suggest a separate flag for that?

@anntzer Thank you for the reminder, I forgot about this discussion here.

for this feature I would also like the displayed name to be used to detect the syntax highlighting to be used

Hmm. The question is if this could lead to surprising/unexpected behavior in other cases.

I think it would now be possible to write a gzip-wrapper with the current implementation of --file-name. When the wrapper is called on a file called path/to/test.cpp.gz, do the following:

  • Create a temporary directory
  • Unzip the path/to/test.cpp.gz file to $tmpdir/test.cpp
  • Run bat --file-name "path/to/test.cpp" "$tmpdir/test.cpp"

I understand that it would be easier if --file-name would also influence the syntax detection, but I guess this could be a reasonable tradeoff?

Something like this:

#!/bin/bash

set -eou pipefail

input_path="$1"
input_gzip_filename="$(basename "$input_path")"
input_filename="${input_gzip_filename%*.gz}"

tmp_dir=$(mktemp -d -t batz-XXXXXXXX)

output_path="$tmp_dir/$input_filename"

gzip --decompress --stdout "$input_path" > "$output_path"

bat --file-name "$input_path" "$output_path"

rm -r "$tmp_dir"

Something like that would work, but then you need to handle the tmpdir cleanup -- if you want to do this properly you (probably) need a trap? It's all doable, just less practical.

I think at least bat could use --file-name for syntax selection when the file is piped to it via stdin and --language is not given, as that in that case there's otherwise no highlighting?

I think _at least_ bat could use --file-name for syntax selection when the file is piped to it via stdin and --language is not given, as that in _that_ case there's otherwise no highlighting?

That sounds reasonable. Could you please open a new feature request? Thank you

@neuronull FYI

done at #891.

Thanks for fixing #891. I think the following works, in case anyone else wants to pick it up:

#!/bin/sh
set -euo pipefail

case "$#" in
    1)
        mode=list
        fname='<unset>'
        ;;
    2)
        mode=extract
        fname="$2"
        ;;
    *)
        echo "Usage: $0 archive-name [file-name]" >&2
        exit
        ;;
esac
aname="$1"
atype="$(file -bi "$aname" | cut -d\; -f1)"
ztype="$(file -Zbi "$aname" | cut -d\; -f1)"
if [[ "$ztype" = application/x-tar ]]; then
    listcmd=(tar --list --file "$aname")
    extractcmd=(tar --extract --to-stdout --file "$aname" "$fname")
elif [[ "$atype" = application/zip ]]; then
    listcmd=(unzip -l "$aname")
    extractcmd=(unzip -p "$aname" "$fname")
else
    echo "Unknown filetype: $atype" >&2
    exit 1
fi
case "$mode" in
    list)
        "${listcmd[@]}" | bat
        ;;
    extract)
        "${extractcmd[@]}" | bat --file-name "$fname"
        ;;
esac
Was this page helpful?
0 / 5 - 0 ratings

Related issues

jkaan picture jkaan  路  3Comments

lilyball picture lilyball  路  3Comments

rien333 picture rien333  路  3Comments

HakubJozak picture HakubJozak  路  3Comments

yannallain picture yannallain  路  3Comments