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 Sep 24, 2022

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 get sushi 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.

GIST='https://gist.github.com/ernstki/0c5979dfd60d68c483772d1d98c3e810'

# assuming ~/bin is in your $PATH, or your login scripts test for it
mkdir -p ~/bin &&
(curl -sL "$GIST/raw/browser" || wget -qO- "$GIST/raw/browser") > ~/bin/browser &&
chmod a+x ~/bin/browser &&
browser --help  # test to make sure it worked

Examples

If only have wget, replace curl -L -H with wget --header; the latter automatically follows redirects, so there's no analogous option to -L.

URL='https://www.meetup.com/Central-NJ-Drupal-Group/photos/927398/'

curl -L "$URL" | browser
curl -L -H 'Cookie: MEETUP_MEMBER=id=' "$URL" | browser

# using Quick Look instead of the default browser
curl -L -H 'Cookie: MEETUP_MEMBER=id=' "$URL" | browser -q

If you symlink qlbrowserbrowser, you can do this instead:

curl -L -H 'Cookie: MEETUP_MEMBER=id=' "$URL" | qlbrowser

@brian-lc
Copy link

brian-lc commented Jun 7, 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. 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 💯

@brian-lc
Copy link

brian-lc commented Jun 7, 2023

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
}

@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