Skip to content

Instantly share code, notes, and snippets.

@nkh
Created March 3, 2017 16:58
Show Gist options
  • Save nkh/d53717d26c425fd4e954fba241ec41e4 to your computer and use it in GitHub Desktop.
Save nkh/d53717d26c425fd4e954fba241ec41e4 to your computer and use it in GitHub Desktop.
#!/bin/bash
# prompt matching function for tmux
# it can be also defined elsewhere
if ! type -t __copy_app_match_my_prompt 1> /dev/null ; then
__copy_app_match_my_prompt() { printf '^(\\\\033\\[[^m]+m)?[[:digit:]]+\ ' ; } # the way it looks for me in a tmux buffer
fi
last_command() { history -p !! | perl -pe 's/^\s*[0-9]+\s*//' | tr -d '\n' ; }
extract_last_command_from_tmux_buffer()
{
# extract last command and its output from tmux buffer
# code can skip section, although it doesn't in this case
read -r -d '' perl_code << 'EOP'
use warnings ;
use strict ;
my ($regex, $display_last_line) = @ARGV ;
my ($skipping_blocks, $started) = (0, 0) ;
LINE:
while (<STDIN>)
{
if ($skipping_blocks)
{
$skipping_blocks-- if /$regex/ ;
}
else
{
if (! $started)
{
#skip the line
$started++ if /$regex/ ;
}
else
{
if(/$regex/)
{
print if $display_last_line ;
last LINE ;
}
else
{
print ;
}
}
}
}
EOP
cat | perl -e "$perl_code" "$(__copy_app_match_my_prompt)" "$1"
}
grab_tmux_pane()
{
local last_command=false last_command_command=0 ansi_codes=
local PARSING=`getopt -o alc --long help -n 'copy' -- "$@"`
eval set -- "$PARSING"
while true ; do
case $1 in
-h | --help) echo "Usage: grab_tmux_pane [-l -a]"
printf "\tgrabs and writes the contents of the tmux pane on stdout\n"
printf "\n"
printf "\t-a\tgrab ansi codes too\n"
printf "\t-l\tgrab last command output\n"
printf "\t-c\tif -l, include the command\n"
printf "\t\tyou will need to define a match_my_prompt function\n"
return ;;
-l ) last_command=true ; shift ;;
-c ) last_command_command=1 ; shift ;;
-a ) ansi_codes=" -e " ; shift ;;
-- ) shift ; break ;;
* ) echo "Internal error while parsing!" ; exit 1 ;;
esac
done
tmux capture-pane -b grab_tmux_pane $ansi_codes -C -J -S - -E -
tmux save-buffer -b grab_tmux_pane /tmp/grab_tmux_pane
tmux delete-buffer -b grab_tmux_pane
if $last_command ; then
tac /tmp/grab_tmux_pane | extract_last_command_from_tmux_buffer $last_command_command | tac
else
cat /tmp/grab_tmux_pane
fi
}
copy_help()
{
cat << 'EOH'
NAME
copy - copies text, a file content, or a command output, to the clipboad
SYNOPSIS
command | copy [options]
copy [options --] [command/file/text]
DESCRIPTION
$ echo "a text" | copy stdin is copied to the clipboard
$ copy some random text copies "some random text" to the clipboad
$ copy file copies content of the file to the clipboard
$ copy command arg arg copies the output of $(command arg arg)
$ copy -- command -opt arg copies the output of $(command -opt arg), use --
so the options go to the command not copy
# in tmmux, grab the output of the last command and the command too
# display how copy copied to the clipboard
$ copy -m -- grab_tmux_pane -l -c
OPTIONS
-m display message about the operation
-t trim new line from output
--file copy file content even if it is executable
--text copy as text even it matches a file name
--help display this help
SEE ALSO
original idea from http://brettterpstra.com/2015/04/27/a-universal-clipboard-command-for-bash/
EOH
}
copy()
{
local file= text= trim_nl=false display_message=false
local PARSING=`getopt -o htm --long help,file,text -n 'copy' -- "$@"`
eval set -- "$PARSING"
while true ; do
case $1 in
-h | --help) copy_help ; return ;;
--file ) file=true ; shift ;;
--text ) text=true ; shift ;;
-t ) trim_nl=true ; shift ;;
-m ) display_message=true ; shift ;;
-- ) shift ; break ;;
* ) echo "Internal error while parsing!" ; exit 1 ;;
esac
done
[[ $1 = "--" ]] && shift
local output
local res=false
local tmpfile="${TMPDIR}/copy.$RANDOM.txt"
local msg=""
if [[ $# == 0 ]]; then
output=$(cat)
msg="Input copied to clipboard"
res=true
else
local cmd=""
for arg in $@; do
cmd+="\"$(echo -en $arg|sed -E 's/"/\\"/g')\" "
done
command_type=$(type "$1" 2> /dev/null)
if [[ $? == 0 && -z $file ]] ; then
output=$(eval "$cmd" 2>&1 /dev/null)
if [[ $? == 0 ]]; then
msg="Results of command '$@' are in the clipboard"
res=true
else
msg="Error running command '$@', $command_type"
res=false
fi
else
if [[ -f $1 && -z $text ]]; then
output=""
for arg in $@; do
if [[ -f $arg ]]; then
type=`file "$arg"|grep -c text`
if [ $type -gt 0 ]; then
output+=$(cat $arg)
msg+="Contents of '$arg' are in the clipboard"
res=true
else
msg+="File '$arg' is not plain text"
fi
fi
done
else
output=$@
msg="Text copied to clipboard"
res=true
fi
fi
fi
if $res ; then
if $trim_nl ; then
echo -ne "$output" | tr -d '\n' | xsel -i -p -s -b
else
echo -ne "$output" | xsel -i -p -s -b
fi
$display_message && echo -e "$msg"
return 0
else
echo -e "$msg"
return 1
fi
}
copy-cleanup()
{
while read x; do unset -f $x; done << EOF
last_command
extract_last_command_from_tmux_buffer
grab_tmux_pane
minimum_version
copy_help
copy
copy-cleanup
EOF
}
minimum_version()
{
current_version="$1" minimum_version="$2"
newest_version=$(echo -e "$current_version\n$minimum_version" | sed '/^$/d' | sort -nrb | head -1)
if [[ "$newest_version" = "$current_version" ]] ; then true ; else false ; fi
}
_bgc_copy () { COMPREPLY=( $( compgen -W 'last_command grab_tmux_pane' -- ${COMP_WORDS[COMP_CWORD]} ) $( compgen -o default -- ${COMP_WORDS[COMP_CWORD]} | sort -n ) ) ; }
if minimum_version "$BASH_VERSION" '4.4' ; then
complete -o nosort -F '_bgc_copy' 'copy'
else
complete -F '_bgc_copy' 'copy'
fi
[[ "${BASH_SOURCE[0]}" = "${0}" ]] && copy $@
copy-cleanup
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment