-
-
Save ernstki/0c5979dfd60d68c483772d1d98c3e810 to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash | |
## | |
## pipe into a browser or a previewer like Quick Look on Mac or Linux | |
## | |
## Authors: Kevin Ernst <ernstki -at- mail.uc.edu> | |
## Geoff Nixon <geoff -at- geoffnixon.net> | |
## Date: 24 September 2022 | |
## See also: https://gist.github.com/defunkt/318247#gistcomment-1196817 | |
## | |
# terminate on errors, unset variables are errors | |
set -eu | |
# set TRACE=1 in the environment to trace execution | |
TRACE=${TRACE:-} | |
(( TRACE )) && set -x | |
ME=${0##*/} | |
HOMEPAGE='https://gist.github.com/ernstki/0c5979dfd60d68c483772d1d98c3e810' | |
SYSTEM=$(uname -s) | |
# how long to sleep in seconds before removing the temp file | |
CLEANUPDELAY=5 | |
# what to use if the '-q' / '--quick' option is given | |
PREVIEWER='sushi' | |
# what to use if BROWSER isn't defined in the environment | |
OPENER='xdg-open' | |
if [[ $SYSTEM == Darwin ]]; then | |
PREVIEWER='qlmanage -p'; OPENER='open' | |
fi | |
if [[ -t 1 ]]; then | |
# use colors if stdout is a terminal | |
BOLD=$(tput bold) | |
UL=$(tput sgr 0 1) | |
RED=$(tput setaf 1) | |
CYAN=$(tput setaf 6) | |
RESET=$(tput sgr0) | |
ERROR="$BOLD${RED}ERROR$RESET" | |
NOTE="$BOLD${CYAN}NOTE$RESET" | |
else | |
BOLD=;UL=;RED=;RESET=;ERROR='ERROR' | |
fi | |
USAGE=" | |
$BOLD$ME$RESET - open stdout (or a file) in a browser or previewer | |
${UL}usage$RESET | |
$ME [-h|--help] | |
$ME [-q|--quick] FILE [FILE...] | |
some-other-command | $ME [-q|--quick] | |
${UL}where$RESET | |
-h, --help prints this help | |
-q, --quick uses previewer ($PREVIEWER) instead of default browser | |
-s, --stay-open stays open and retains the temp file until a key is | |
pressed; useful when 'browser' is used in a pipe | |
-k, --keep retains the temp file after exit | |
${UL}homepage$RESET | |
$HOMEPAGE | |
" | |
# respect BROWSER if set | |
opener=${BROWSER:-$OPENER} | |
inputs=() | |
# print line number if 'set -e' causes the script to terminate prematurely | |
trap 'echo -e "\n$ERROR: Script error at $BASH_SOURCE line #$LINENO.\n" >&2' ERR | |
# default to preview behavior if called as 'ql<something>' | |
if [[ $ME =~ ^ql ]]; then | |
set -- --quick "$@" | |
fi | |
stay= | |
keep= | |
while (( $# )); do | |
case $1 in | |
-h|--help) | |
echo "$USAGE" | |
exit | |
;; | |
-q|--quick) | |
if [[ $(type -t ${PREVIEWER%% *}) ]]; then | |
opener=$PREVIEWER | |
else | |
echo -e "\n$ERROR: No previewer found; see PREVIEWER in $BASH_SOURCE.\n" >&2 | |
exit 1 | |
fi | |
;; | |
-s|--stay*) | |
stay=1 | |
;; | |
-k|--keep) | |
keep=1 | |
;; | |
-*) | |
echo -e "\n$ERROR: Unsupported option '$1'. See '--help'.\n" >&2 | |
exit 1 | |
;; | |
*) | |
inputs+=("$1") | |
;; | |
esac | |
shift | |
done | |
if [[ ${#inputs[@]} -eq 0 ]]; then | |
if [[ -t 0 ]]; then | |
echo -e "$ERROR: At least one file argument is required." | |
echo "$USAGE" >&2 | |
exit 1 | |
fi | |
# otherwise, use stdin | |
inputs=('-') | |
fi | |
for input in "${inputs[@]}"; do | |
# FIXME: how to make this work for process substitutions? | |
if [[ -r $input && ( -f $input || -L $input ) ]]; then | |
# don't create an extra temp file if the file *is* a file | |
if (( stay )); then | |
echo "$NOTE: '--stay-open' is only useful in a pipe. See '--help'." >&2 | |
fi | |
if (( TRACE )); then | |
$opener "$input" | |
else | |
# suppress stdout and stderr by default; 'qlmanage' on macOS is | |
# quite chatty, for example | |
$opener "$input" &>/dev/null | |
fi | |
else | |
# macOS/BSD `mktemp` doesn't support a suffix, so this rigmarole | |
tmp=$(mktemp "${TMPDIR:=/tmp}/$ME-XXXXXXXX") | |
mv "$tmp"{,.html}; tmp="$tmp.html" | |
if (( !keep )); then | |
trap "(sleep $CLEANUPDELAY; rm '$tmp')&" EXIT | |
fi | |
umask 077 # protect temp file from prying eyes | |
cat "$input" > "$tmp" | |
if (( TRACE )); then | |
$opener "$tmp" | |
else | |
$opener "$tmp" &>/dev/null | |
fi | |
if (( stay )); then | |
if (( keep )); then | |
echo "$NOTE: '--stay-open' serves no purpose when used with '--keep'. See '--help'." >&2 | |
fi | |
# FIXME: does '</dev/tty' work on macOS/BSD, too? | |
read -s -N1 -p$'Press a key to continue...\n' < /dev/tty | |
fi | |
fi | |
done |
This is a very useful script! Thanks! One issue I encountered was on my Mac (M2 Ventura 13.4). The base64
command now seems to require explicit -i
for input and -o
for output. Also, you can just get X characters from /dev/urandom
with head -c6
, so that seemed more efficient. Changing line 107 to:
tmp="/tmp/$ME-$(head -c6 /dev/urandom | base64 -i - -o - | tr -dc '[[:alnum:]]').html"
made it all work flawlessly 💯
Also (on mac) paired with brew install markdown
you can write a little repo readme reader function that's handy at the terminal. Drop it in your .bashrc or .zshrc file and go! Not super sophisticated, but repos tend to follow the github convention for readme's.
rrm() {
markdown README.md | browser
}
This is a very useful script! Thanks! One issue I encountered was on my Mac (M2 Ventura 13.4). The
base64
command now seems to require explicit-i
for input and-o
for output.
@brian-lc The more I stared at it, the more puzzled I became about why the original author did that instead of just using mktemp
. Do you have any insights?
Anyway, I removed all the base64
stuff entirely to avoid the problem. If the updated version still works in your testing (I'm still using some old busted version of macOS), then I think we can put this one to rest.
Use on Linux
You may be able to use GNOME Sushi (RIP Gloobus Preview) from your distro's package manager to achieve the
-q
/--quick
behavior. In my own testing (with Pop!_OS 22.04 and Fedora 34), I couldn't getsushi
to display the actual web page, though.Installation
You need Bash installed on your system. It doesn't matter where it is, as long as you can run
bash
from the shell and not get a "command not found" error.Examples
If only have
wget
, replacecurl -L -H
withwget --header
; the latter automatically follows redirects, so there's no analogous option to-L
.If you symlink
qlbrowser
→browser
, you can do this instead: