Skip to content

Instantly share code, notes, and snippets.

@ernstki
Last active August 30, 2023 14:42
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ernstki/0c5979dfd60d68c483772d1d98c3e810 to your computer and use it in GitHub Desktop.
Save ernstki/0c5979dfd60d68c483772d1d98c3e810 to your computer and use it in GitHub Desktop.
Pipe into a browser, or a previewer like Quick Look (macOS and Linux)
#!/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
@ernstki
Copy link
Author

ernstki commented Aug 27, 2023

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment