Last active
July 22, 2019 19:15
-
-
Save ernstki/a2e8237a0fdede6a8e553495b1d08301 to your computer and use it in GitHub Desktop.
doclip - manipulate clipboard text easily from the command line (macOS and Linux)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# | |
# Manipulate clipboard contents easily; optionally ensures a final newline is | |
# written to the output | |
# | |
# NB: If symlinked as 'reclip[.sh]', automatically re-copies to the clipboard | |
# (just as if you specified '-r') | |
# | |
# Author: Kevin Ernst <ernstki -at- mail.uc.edu> | |
# URL: https://gist.github.com/ernstki/a2e8237a0fdede6a8e553495b1d08301 | |
# | |
set -ueo pipefail | |
# set TRACE=1 in the environment to trace script execution | |
(( ${TRACE:-} )) && set -x | |
DEBUG=${DEBUG:-} | |
ME=$( basename "$BASH_SOURCE" ) | |
USAGE=" | |
usage: | |
$ME [-h|--help] [-c|--columns] [-m|--max-line-length] | |
$ME [-r|--recopy] [-n|--newline] [-x|--xargs] [<cmd> | -s|--sed <expr>] | |
where: | |
-h, --help you're looking at it ;) | |
-c, --columns show column/character count of clipboard contents; good for | |
checking the length of a line (shortcut for 'doclip wc -m') | |
-m, --max-line-length | |
compute and display the maximum length for all the input | |
lines; like 'wc -L' (but that's GNU/Linux-only) | |
-r, --recopy re-copy results to the clipboard (default: print to stdout) | |
-n, --newline ensure output ends with a newline (not needed w/ 'sed') | |
-x, --xargs run <cmd> as many time as there are input lines, each time | |
with one input line as its argument, properly quoted; if | |
order doesn't matter, add '-P <nprocs>' to speed things up | |
<cmd> pipe clipboard contents through this command, which may be | |
a quoted string, including pipes (e.g., 'par w60') | |
-s <expr> expression fed directly to 'sed'; also used to prevent | |
auto-globalization of a detected sed 's///' expression | |
(see below) | |
When called as 'reclip' (create a symlink to do this), this script behaves | |
as if the '-r' (re-copy results) option had been given. | |
Pass '--' after any 'doclip' options to treat any further arguments as part | |
of the command, e.g., 'doclip -r -- xargs du -s \\| sort -r -n -k1' | |
(where '-s' and '-n' would otherwise be recognized as 'doclip' options). | |
If <expr> begins with 's/', the 'g' flag is appended for you if you forget, | |
and the '-s' switch may be omitted (try 's/ /_/'); if you don't want your | |
sed expression messed with, specify '-s' / '--sed'. | |
" | |
LITERALSED= | |
ADDNEWLINE= | |
RECOPY= | |
XARGS= | |
STOPARGS= | |
CMD=() | |
sedexp= | |
# on macOS/BSD, piping through 'sed' will always add a terminating newline | |
function newliner() { sed; } | |
# compute maximum length of all input lines | |
function maxlength() { awk '{if (length>max) {max=length}} END{print max}'; } | |
# quote input lines to protect spaces / metacharacters | |
#function shellquote() { printf "%q" "$1"; } | |
if [[ `uname -s` != Darwin ]]; then | |
if ! which xclip &>/dev/null; then | |
echo >&2 | |
echo "ACK! Missing required external utility 'xclip'." >&2 | |
echo " Try installing it with your distro's package manager." >&2 | |
echo >&2 | |
exit 1 | |
fi | |
function pbcopy() { xclip -i -sel clip; } | |
function pbpaste() { xclip -o -sel clip; } | |
# on Linux, use Perl to ensure every line ends with a LF character | |
function newliner() { perl -ne 'chomp; print "$_\n"'; } | |
fi | |
# redundant but still allowed if '-c' is supplied; Bash has no 'unshift' | |
[[ $ME =~ ^reclip ]] && RECOPY=1 | |
while (( $# )); do | |
case $1 in | |
--) | |
STOPARGS=1 | |
;; | |
-*) | |
# don't process any options flags after '--' given | |
if (( STOPARGS )); then | |
CMD+=("$1") | |
shift; continue | |
fi | |
# otherwise, prcess 'doclip' options | |
case $1 in | |
# help | |
-h|-\?|--h*) | |
echo "$USAGE" | |
exit | |
;; | |
# column count | |
-c|--c*) | |
CMD=(wc -m) | |
;; | |
# max line length | |
-m|--max*) | |
CMD=(maxlength) | |
;; | |
# copy back to clipboard | |
-r|--r*) | |
RECOPY=1 | |
echo "Re-copying results back to clipboard." >&2 | |
;; | |
# (literal) sed expression | |
-s|--s*) | |
shift | |
LITERALSED=1 | |
CMD=(sed "$1") | |
;; | |
# ensure output ends in a newline | |
-n|--n*) | |
ADDNEWLINE=1 | |
;; | |
# send input through 'xargs -L1' | |
-x|--x*) | |
XARGS=1 | |
;; | |
# Unknown options assumed to be for the program... | |
*) | |
CMD+=("$1") | |
;; | |
# ...if you want to *force* the use of '--' to separate them, | |
# uncomment this | |
#*) | |
# echo >&2 | |
# echo "ACK! Unknown $ME option '$1'." >&2 | |
# echo " Maybe try '$ME -- program <args>' instead?" >&2 | |
# echo >&2 | |
# exit 1 | |
# ;; | |
esac | |
;; | |
s/*) | |
sedexp=$1 | |
# if user gave '-s' / '--sed', don't mess with it | |
# otherwise, make it global, unless it already was | |
if (( !LITERALSED )); then | |
[[ $1 =~ /g$ ]] || sedexp="$1g" | |
fi | |
CMD=(sed "$sedexp") | |
;; | |
*) | |
CMD+=("$1") | |
;; | |
esac | |
shift | |
done | |
cmd='pbpaste' | |
if (( XARGS )); then | |
# somehow this actually works; arguments w/ spaces are quoted, and | |
# '${CMD[@]}' gets expanded and individually double-quoted when 'eval'd | |
cmd+=' | xargs -L 1 -I {} "${CMD[@]}" {}' | |
else | |
# if CMD is empty, don't even include it in the pipeline; otherwise ensure | |
# the arguments are individually quoted once expanded in the 'eval' | |
[[ ${CMD:-} ]] && cmd+='| "${CMD[@]}"' | |
fi | |
# if ADDNEWLINE=1, use the 'newliner' function to guarantee a final newline | |
# if RECOPY=1, pipe through 'pbcopy' to put result back in clipboard | |
(( DEBUG )) && set -x | |
eval "$cmd ${ADDNEWLINE:+| newliner} ${RECOPY:+| pbcopy}" | |
set +x |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Some useful sed scripts that you can put in your
PATH
for use withdoclip
:Also see
unwrap
(remove line breaks),recase
(upper- or lower-case input text based on what name the script is called as),reslashify
(convert forward ↔︎ backslashes in a pathname), andtitlecase
(title-case the input according to AP or Chicago style guides).