Home Page
Archive > Posts > Tags > Bash
Search:

My Tmux config

Tmux is a great alternative to gnu screen. I can’t believe I’ve never posted my custom Tmux config for Cygwin after all the work I put into it years ago. So here it is. Its features include:

  • Uses ctrl+a, like gnu screen, instead of ctrl+b
  • Mouse interaction is enabled
  • Tab bar/windows:
    • Current tab is highlighted in cyan
    • Cycle through tabbed windows with a click on its tab or ctrl+arrowkeys
    • Reorder tabbed windows with a drag of its tab or alt+arrowkeys
    • ctrl+a,/ to rename a tab on the tab bar
    • Create new window with ctrl+a,c
  • Panes
    • Create split panes with vertical=ctrl+a,| and horizontal=ctrl+a,-
    • Move around panes with click or ctrl+shift+arrowkeys
    • Resize panes by dragging on the separator bar or use ctrl+shift+alt+arrowkeys
    • Panes automatically resize to fit OS window
  • Clipboard/highlighting
    • Copy text to clipboard by highlighting it. Had to use a minor hack to fix a cygwin selection problem
    • Paste from clipboard with right click
    • Middle mouse button+drag starts copy mode
      • When in copy mode, u runs the selection as a command in a separate window (Instead of “cygstart” for cygwin, use “xdg-open” for linux, or “open” for MacOS X)
    • Double click selects word
    • Double middle click runs the word under the mouse as a command
  • Start the session on the current bash directory
  • Escape time is lowered for quicker response to scroll buffer access (ctrl+a,pageup)

To use this, save the file to ~/.tmux.conf

#Set current directory setting for cygwin
set-environment -g CHERE_INVOKING 1

#Mouse interaction
set -g mouse on

#Lower escape timing from 500ms to 50ms for quicker response to scroll-buffer access
set -s escape-time 50

#Window always takes up largest possible max size
set-window-option -g aggressive-resize

#Highlight active window in tab-bar at bottom in cyan
set-window-option -g window-status-current-bg cyan

#Reorder windows in status bar by drag & drop
bind-key -n MouseDrag1Status swap-window -t=

#Copy to clipboard on text selection in cygwin. Move cursor position 1 to the right before copy to bypass a bug
bind -Tcopy-mode MouseDragEnd1Pane send-keys -X cursor-right\; send -X copy-selection-and-cancel\; run-shell -b "tmux show-buffer > /dev/clipboard"

#Paste from clipboard with right click in cygwin
bind-key -n MouseDown3Pane run-shell 'tmux set-buffer -b winclip "$(cat /dev/clipboard)"'\; paste-buffer -db winclip

#Middle drag starts copy mode
bind -n MouseDrag2Pane copy-mode -M

#When in copy mode, "u" runs the selection as a command in a separate window (Instead of "cygstart" for cygwin, use "xdg-open" for linux, or "open" for MacOS X)
bind -Tcopy-mode u send -X copy-selection-and-cancel\; run-shell -b "tmux show-buffer | xargs cygstart"

#Double click selects word
bind-key -n DoubleClick1Pane copy-mode -M\; send-keys -X select-word

#Double middle click runs the word under the mouse as a command. See description for MouseDown3Pane above
bind-key -n DoubleClick2Pane copy-mode -M\; send-keys -X select-word\; send -X copy-selection-and-cancel\; run-shell -b "tmux show-buffer | xargs cygstart"

#Remap prefix to Control+a
set -g prefix C-a
unbind C-b
#bind 'C-a C-a' to type 'C-a'
bind C-a send-prefix

#Start in CWD when creating or splitting tabs; move the splitting planes keys to | and -
bind '|' split-window -h -c '#{pane_current_path}'  # Split panes horizontal
bind '-' split-window -v -c '#{pane_current_path}'  # Split panes vertically
bind c new-window -c '#{pane_current_path}' # Create new window
unbind '"'
unbind %

#prefix, / -- Renames window, but starts blank
bind-key / command-prompt "rename-window '%%'"

#Select next/prev window with Ctrl+(Left|Right)
bind-key -n C-Right next-window
bind-key -n C-Left previous-window

#Reorder window with Alt+(Left|Right)
bind-key -n M-Left swap-window -t -1
bind-key -n M-Right swap-window -t +1

#Switch panes using Ctrl+Shift+arrow
bind -n C-S-Left select-pane -L
bind -n C-S-Right select-pane -R
bind -n C-S-Up select-pane -U
bind -n C-S-Down select-pane -D

#Resize panes using Ctrl+Shift+Alt+arrow
bind-key -n C-S-M-Up resize-pane -U 1
bind-key -n C-S-M-Down resize-pane -D 1
bind-key -n C-S-M-Left resize-pane -L 1
bind-key -n C-S-M-Right resize-pane -R 1
Reading Mailchimp batch request results

It’s a bit of a pain reading results from batch requests to Mailchimp. Here is a quick and dirty bash script to get and pretty print the JSON output. It could be cleaned up a little, including combining some of the commands, but meh.


#Example variables
BATCHID=abc1234567;
APIKEY=abcdefg-us11@us11.api.mailchimp.com;
APIURL=us11.api.mailchimp.com;

#Request the batch information from Mailchimp
curl --request GET --url "https://dummy:$APIKEY@$APIURL/3.0/batches/$BATCHID" 2> /dev/null | \

#Get the URL to the response
grep -oP '"response_body_url":"https:.*?"' | \
grep -oP 'https:[^"]*' | \

#Get the response
xargs wget -O - 2> /dev/null | \

#The response is a .tar.gz file with a single file in it. So get the contents of this file
tar -xzvO 2> /dev/null | \

#Pretty print the json of the full return and the “response” objects within
php -r '$Response=json_decode(file_get_contents("php://stdin"), true); foreach($Response as &$R) $R["response"]=json_decode($R["response"], true); print json_encode($Response, JSON_PRETTY_PRINT);'
MD5Sum List Script
#This script takes a newline delimited file list from STDIN for md5 hashing
#This script requires the `md5sum`, `pv`, `paste`, `bc`, and 'numfmt' commands

#The output of the md5s are stored in the file specified by the first parameter
#The format for each md5 hash to the output file is "$FileName\t$Hash\n"

#File sizes are always output in megabytes with 3 decimal places
#While calculating the hashes the script keeps the user informed of the progress of both the current file and all the files as follows:
#1) Before file starts: "Hashing: $FileName ($FileSize MiB)\n"
#2) During transfer: The progress of the hash of the current file ran through `pv`
#3) During transfer: The progress of the hashing of all the files, ran through `pv`
#4) After transfer: "Finished $TotalProgressPercent% ($ProcessedBytes/$TotalBytes MiB)\n\n"

#Get $Outfile from the first argument and the $FileList from STDIN (newline delimited)
OutFile="$1";
FileList=`cat /dev/stdin`

#Format a byte count in MegaBytes with comma grouping and 3 decimal places
MbFmtNoExt ()
{
	echo "scale=3; $1/1024/1024" | bc | echo -n `xargs numfmt --grouping`
}

#Add " MiB" to the end of MbFmtNoExt
MbFmt ()
{
	echo `MbFmtNoExt $1`" MiB"
}

#Calculate and output the total size of the file list
echo -n "Calculating total size: "
TotalSize=`echo "$FileList" | xargs -d"\n" stat --printf="%s\n" | paste -s -d+ | bc`
MbFmt $TotalSize
echo #Add an extra newline

#Create a fifo to keep track of the total complete
TotalDoneFifo=$(mktemp)
TotalDoneBG=0
rm "$TotalDoneFifo"
mkfifo "$TotalDoneFifo"
cat > "$TotalDoneFifo" & #Do not close read of fifo
Cleanup() {
	rm "$TotalDoneFifo"
	kill $TotalDoneBG
	exit 0
}
trap Cleanup SIGTERM SIGINT

#Start the TOTAL line
tail -f "$TotalDoneFifo" | pv -s $TotalSize -F  "%b %t %p %e" > /dev/null &
TotalDoneBG=$!

#Run over the list (newline delimited)
CalculatedBytes=0
IFS=$'\n'
for FileName in `echo "$FileList"`
do
	#Output the file size and name to STDOUT
	FileSize=`stat --printf="%s" "$FileName"`
	echo "Hashing: $FileName ("`MbFmt $FileSize`")"

	#Output the filename to $OutFile
	echo -n $FileName$'\t' >> $OutFile

	#Run the md5 calculation with `pv` progress
	#Output the hash to $OutFile after the FileName and a tab
	cat "$FileName" | pv -s $FileSize -c | tee -a "$TotalDoneFifo" | md5sum | awk '{print $1}' >> $OutFile

	#Output the current progress for the entire file list
	#Format: "Finished $TotalProgressPercent% ($ProcessedBytes/$TotalBytes MiB)\n\n"
	CalculatedBytes=$(($CalculatedBytes+$FileSize))
	echo -n "Finished "
	printf "%.3f" `echo "scale=4; $CalculatedBytes*100/$TotalSize" | bc`
	echo "% ("`MbFmtNoExt $CalculatedBytes`"/"`MbFmt $TotalSize`$')\n'
done

Cleanup
Ping Connectivity Monitor

The following is a simple bash script to ping a different domain once a second and log the output. By default, it pings #.castledragmire.com, where # is an incrementing number starting from 0.

The script is written for Cygwin (See the PING_COMMAND variable at the top) but is very easily adaptable to Linux.

The log output is: EPOCH_TIMESTAMP DOMAIN PING_OUTPUT


#This uses Window's native ping since the Cygwin ping is sorely lacking in options
#"-n 1"=Only runs once, "-w 3000"=Timeout after 3 seconds
#The grep strings are also directly tailored for Window's native ping
PING_COMMAND=$(
	echo 'C:/Windows/System32/PING.EXE -n 1 -w 3000 $DOMAIN |';
	echo 'grep -iP "^(Request timed out|Reply from|Ping request could not find)"';
)

i=0 #The subdomain counter
STARTTIME=`date +%s.%N` #This holds the timestamp of the end of the previous loop

#Infinite loop
while true
do
	#Get the domain to run. This requires a domain that has a wildcard as a primary subdomain
	DOMAIN="$i.castledragmire.com"

	#Output the time, domain name, and ping output
	echo `date +%s` "$DOMAIN" $(eval $PING_COMMAND)

	#If less than a second has passed, sleep up to 1 second
	ENDTIME=`date +%s.%N`
	SLEEPTIME=$(echo "1 - ($ENDTIME - $STARTTIME)" | bc)
	STARTTIME=$ENDTIME
	if [ $(echo "$SLEEPTIME>0" | bc) -eq 1 ]; then
		sleep $SLEEPTIME
		STARTTIME=$(echo "$STARTTIME + $SLEEPTIME" | bc)
	fi

	#Increment the subdomain counter
	let i+=1
done