Skip to content

Instantly share code, notes, and snippets.

@deanrather
Last active June 25, 2022 06:28
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save deanrather/5719199 to your computer and use it in GitHub Desktop.
Save deanrather/5719199 to your computer and use it in GitHub Desktop.
Configure Work Environment
#!/bin/bash
# Workstation Configurator
# To setup this script, run:
#
# wget -O ~/.workstation git.io/workstation && . ~/.workstation setup
#
# This achieves a few things:
# - Installation & Setup of Programs (Interactive)
# - Provides ~/.workstation file which automatically loads function library
# - Provides ~/.workstation.d directory for auto-loading your own scripts
#
# This script has been designed to demonstrate lots of various bash functionality,
# and to include annotated examples, and references.
#
# For help, run:
#
# workstation help
## CONFIGURATION ##
# Packages to install
package_list="git ssh tig vim tree xclip xdotool screen autossh lsyncd curl wget tmux"
# Configure History to automatically save
export HISTCONTROL=ignoredups:erasedups # no duplicate entries
export HISTSIZE=100000 # big big history
export HISTFILESIZE=100000 # big big history
shopt -s histappend # append to history, don't overwrite it
# Save and reload the history after each command finishes
export PROMPT_COMMAND="history -a; history -c; history -r; $PROMPT_COMMAND"
# Configure history to save with timestamp
export HISTTIMEFORMAT="%Y-%m-%d %T "
# Bash Prompt Colour
# https://wiki.archlinux.org/index.php/Color_Bash_Prompt
set_prompt()
{
local previous_exit_code=$?
local blue='\[\e[01;34m\]'
local red='\[\e[01;31m\]'
local green='\[\e[01;32m\]'
local white='\[\e[00m\]'
local cross='\342\234\227'
local tick='\342\234\223'
local git_branch="$(git branch 2> /dev/null | grep '*' | awk '{print $NF}' | sed 's/)//g')"
PS1=""
# Tick or a cross depending on the previous exit code
[[ $previous_exit_code == 0 ]] && PS1+="$green$tick" || PS1+="$red$cross"
# PS1+="$white|"
PS1+=" "
# Host, or user@host depending on user
[[ $EUID == 0 ]] && PS1+="$red\\h" || PS1+="$green\\u@\\h"
PS1+="$white:"
# Working directory
PS1+="$blue\\w"
# Green or red git branch, depending on dirty
if [ -n "$git_branch" ]
then
# PS1+="$white|"
PS1+=" "
[[ -z $(git status --porcelain) ]] && PS1+="$green" || PS1+="$red"
PS1+="$git_branch"
fi
# Print the special dollar sign, and a space
PS1+="$white\\\$ "
}
PROMPT_COMMAND='set_prompt'
# Generating Help
# This saves a list of defined functions to the variable
# It will be compared to a list we'll make futher down,
# so we can know which functions were defined.
[[ "$original_function_list" ]] || original_function_list=$(compgen -A function)
## MISC FUNCTIONS ##
# Writes a log to ~/.workstation.log/
# Usage: echo <message> | log [<logfile name>]
# eg: echo "hello world" | log
# eg: echo "hello world" | log mylog.log
log()
{
local message
# Read message from STDIN
read message
# If dir does not exist, create it
[ -d ~/.workstation.log ] || mkdir ~/.workstation.log
# Set provide default value for first argument
[ -z "$1" ] && log_basename='workstation.log' || log_basename="$1"
# Concatenate dir with basename
local log_path=~/.workstation.log/"$log_basename"
# Create logfile if it doesn't exist
touch "$log_path"
# Get current time in ISO8601 format
# eg: 2014-08-29T19:01:46+10:00
local date=$(date +%F\T%T%z | sed 's/^.\{22\}/&:/')
# Get process ID
local pid=$$
# Append message to log file
echo "$date $pid $message" >> "$log_path"
}
# Backs up a file (creates a copy in the same dir with <name>.bak-<timestamp>)
# Usage: backup /path/to/file
backup()
{
cp "$1" "$1.bak-$(date --utc +%Y%m%d_%H%M%SZ)"
}
# Executes a command repeatedly
# usage: repeat <command> [<frequency in seconds>]
# CTRL+c to quit
# eg: repeat date
# Note that this is similar to `watch`
repeat()
{
echo "TODO. fix me"; return
local command=$1 # command is first arg
local frequency=${2:-1} # frequency is second arg or "1"
clear
trap ctrl_c INT # catch ctrl+c keypress
function ctrl_c
{
clear
tput cnorm # put the cursor back to normal
return
}
while [ true ] # Repeat forever
do
tput civis # hide the cursor
tput cup 0 0 # put cursor at top-left
$command # execute the command
sleep $frequency # wait for frequency
done
}
# Watches a directory (recursively) for changes
# If any files within the directory change, executes command
# Usage: execute_on_change <dir> <command>
# eg: execute_on_change /tmp "echo yep"
execute_on_change()
{
# see also: https://gist.github.com/senko/1154509
local path="$1"
local command="$2"
local current_hash=""
local new_hash=""
while [[ true ]]
do
new_hash="$(find "$path" -type f | md5sum)"
if [[ $old_hash != $new_hash ]]
then
$command
old_hash=$new_hash
fi
sleep 2
echo "hash: $new_hash"
done
}
# Displays "Press [ENTER] to cancel..."
# Returns:
# true after a 4 second timeout
# false if the user presses enter
# eg:
# echo "about to do the thing"
# if enter_to_cancel
# then
# echo "doing the thing"
# fi
enter_to_cancel()
{
# Display the message
echo -n "Press [ENTER] to cancel"
# Give them some time to read
local i
for i in 1 2 3 4
do
# If it's not the first second, display a dot
[ "$i" -gt 1 ] && echo -n "."
# Wait 1 second, if the user enters something return false
read -t 1 && return 1
done
# end the line
echo
# nothing was pressed, return true
return 0
}
# Get internet IP address
getip_public()
{
curl http://ipecho.net/plain
echo
}
# Get local IP address
# Usage: getip [<interface name>]
getip()
{
# interface provided as $1, defaults to "eth0"
local interface=${1:-eth0}
ifconfig "$interface" | grep -oP '(?<=inet addr:).*?(?= )'
# -o # output the match
# -P # use pearl regex
# (?<=PATTERN) # begin selection after this pattern
# .*? # select everything until first match
# (?=PATTERN) # end selection before this pattern
}
# Sets a static IP address
# Backs up existing interfaces file
# Usage: setip <fourth IP tuplet> [<interface>]
# eg: setip 99
# eg: setip 50 wlan0
setip()
{
local path="/etc/network/interfaces"
# TODO: Error if no $1 is passed
local interface=${2:-eth0}
# TODO:
# - detect the list of interfaces
# - if only 1; use that; otherwise throw error
# ifconfig -s -a | awk '{print $1}'
# Get the existing IP address parts
local p1 p2 p3 p4
read p1 p2 p3 p4 <<< $(getip "$interface" | tr "." "\n")
# Set the fourth tuplet to that provided
p4=$1
# Backup existing file
sudo cp "$path"{,.bak}
# Write new file
sudo sh -c "echo 'auto lo' > $path"
sudo sh -c "echo 'iface lo inet loopback' >> $path"
sudo sh -c "echo '' >> $path"
sudo sh -c "echo 'auto $interface' >> $path"
sudo sh -c "echo 'iface $interface inet static' >> $path"
sudo sh -c "echo ' address $p1.$p2.$p3.$p4' >> $path"
sudo sh -c "echo ' network $p1.$p2.$p3.0' >> $path"
sudo sh -c "echo ' netmask 255.255.255.0' >> $path"
sudo sh -c "echo ' broadcast $p1.$p2.$p3.255' >> $path"
sudo sh -c "echo ' dns-nameservers $p1.$p2.$p3.255' >> $path"
sudo sh -c "echo ' gateway $p1.$p2.$p3.1' >> $path"
echo "IP address $p1.$p2.$p3.$p4 written to $path"
# Restart the network
# TODO: Only restart specific interface
echo "Restarting network"
if enter_to_cancel
then
sudo ifdown -a
sudo ifup -a
fi
}
# Creates a new branch
# Usage: git branch <branchname>
git_branch()
{
echo "git checkout -b $1"
git checkout -b $1
echo "git push -u origin $1"
git push -u origin $1
}
# Run a program in the background
# Usage: run_in_background <command>
run_in_background()
{
$@ > /dev/null 2>&1 &
echo "Job running in background with pid: $!"
}
# Shortens a GitHub URL
# Usage: github_shortenurl <github url> [<code>]
github_shortenurl()
{
# TODO:
# - Warn if url looks like a commit instead of a head
# - Warn if no /raw part on the end
# - Output only the new URL if it worked
local long_url short_url code orl_arg code_arg response
if [ -n "$1" ]
then
long_url="$(echo $1 | sed 's/githubusercontent/github/g')"
else
describe_function "$FUNCNAME"
return 1
fi
if [ -n "$2" ]
then
curl -o- -i -s http://git.io -F "url=$long_url" -F "code=$2"
else
curl -o- -i -s http://git.io -F "url=$long_url"
fi
echo
}
# Set the terminal title
set_term_title()
{
echo -ne "\033]0;$1\007"
}
# Clone from github
# Usage: github_clone username/repo
github_clone()
{
git clone git@github.com:/$1.git
}
# Clone from bitbucket
# Usage: bitbucket_clone username/repo
bitbucket_clone()
{
git clone git@bitbucket.com:/$1.git
}
# Opens a google-chrome browser and googles for the query
# Usage: google <query>
# eg: google shell scripting
google()
{
local command="google-chrome https://www.google.com.au/search?q=$1&btnl=1"
run_in_background "$command"
}
# # Copies the current terminal line
# # Bound to ALT+C
# copy_current_line()
# {
# local current_line="${READLINE_LINE:0:$READLINE_POINT}${CLIP}${READLINE_LINE:$READLINE_POINT}"
# echo -n "$current_line" | xclip
# }
# bind -m emacs -x '"\ec": copy_current_line' || echo "unable to bind for copy-line"
# Copies the last command executed
copy_last_command()
{
fc -ln -1 | sed 's/^ *//' | xclip # copies to middle-click-paste
# fc -ln -1 | sed 's/^ *//' | xclip -selection clipboard # copies to regular paste
}
# View active network connections
view_network()
{
lsof -i
}
# Displays a hash of a directory recursively
# Usage: hashdir <path/to/dir>
hashdir()
{
local dir="$1"
find "$dir" -type f -exec md5sum {} + | awk '{print $1}' | sort | md5sum
}
# Displays the final text block from a file
# Usage: display_final_block <file>
# eg: display_final_block ~/.workstation
function display_final_block()
{
local file="$1"
local description="$2"
echo -en "\n\n$description\n\n"
local line
tac "$file" | while read line
do
[[ "$line" ]] || break
echo -en "\t$line\n"
done | tac
}
# Describes a function
# Usage: describe_function <function name>
# eg: describe_function display_final_block
function describe_function()
{
local function_name=$1
# Turn on extended shell debugging
shopt -s extdebug
# Get the line number defined on
local line_number="$(echo $(declare -F $1) | awk '{print $2}')"
# Get the file defined in
local file="$(echo $(declare -F $1) | awk '{print $3}')"
# Turn off extended shell debugging
shopt -u extdebug
# tmp hax
function_name="$(echo $function_name | sed 's/_workstation_/workstation /g')"
echo -en "\n\t$function_name\n"
let "line_number-=1"
local line
head -n "$line_number" "$file" | tac | while read line
do
[[ "$line" ]] || break
line="$(echo $line | sed 's/^# //')"
# echo $line
echo -en "\t\t$line\n"
done | tac
let "line_number+=1"
echo -en "\t\t > $file +${line_number}\n"
}
# Describes a list of functions
# Usage: describe_functions <function list> <list name>
describe_functions()
{
local function_list=$1
local function_list_name=$2
echo -en "\n\n$function_list_name\n"
local function_name
echo "$function_list" | while read function_name
do
describe_function "$function_name"
done
}
# Get list of misc functions defined
[[ "$misc_function_list" ]] ||
misc_function_list=$(grep -Fxv -f \
<(echo "$original_function_list") \
<(compgen -A function))
## OTHER SCRIPTS ##
# Source any scripts in the ~/.workstation.d directory
[ -d ~/.workstation.d ] || mkdir ~/.workstation.d
if [ "$(ls -A ~/.workstation.d)" ]
then
for script in ~/.workstation.d/*
do
source "$script"
done
fi
unset script
# Get list of other functions defined
[[ "$other_function_list" ]] ||
other_function_list=$(grep -Fxv -f \
<(echo -e "$original_function_list\n$misc_function_list") \
<(compgen -A function))
## WORKSTATION FUNCTIONS ##
# Displays list of functions defined by ~/.workstation
_workstation_help()
{
local help=''
help+="$(describe_functions "$workstation_function_list" "Workstation functions:")"
help+="$(describe_functions "$misc_function_list" "Misc functions:")"
help+="$(describe_functions "$other_function_list" "Other functions:")"
help+="$(display_final_block ~/.workstation "Notes:")"
less -qf <(echo "$help")
}
_workstation_debug()
{
bash -e ~/.workstation
}
# This is only executed when run with the wget command at the top of the script,
# or when passing in the "setup" argument.
# It is not run when auto-loaded by your profile.
_workstation_setup()
{
# Install packages
# Uses sed to find/replace spaces with space-comma.
echo "Installing packages: $(echo $package_list | sed 's/ /, /g')"
sudo apt-get update -y
sudo apt-get install $package_list -y
unset package_list
if [ -z "$(git config --global user.name)" ]
then
echo -n "Git user name: "
local git_user_name
read git_user_name
git config --global user.name "$git_user_name"
fi
if [ -z "$(git config --global user.email)" ]
then
echo -n "Git user email: "
local git_user_email
read git_user_email
git config --global user.email "$git_user_email"
fi
echo "Configuring Git"
git config --global color.ui true
git config --global push.default matching
echo "Configuring Tig"
touch ~/.tigrc
grep -q "color date" ~/.tigrc || echo "color date white black" >> ~/.tigrc
grep -q "color graph-commit" ~/.tigrc || echo "color graph-commit red black" >> ~/.tigrc
echo "Configuring Vim"
touch ~/.vimrc
grep -q "set background" ~/.vimrc || echo "set background=dark" >> ~/.vimrc
grep -q "set tabstop" ~/.vimrc || echo "set tabstop=4" >> ~/.vimrc
grep -q "map <F5>" ~/.vimrc || echo "map <F5> :!php -l %<CR>" >> ~/.vimrc
echo "Configuring Screen"
touch ~/.screenrc
grep -q "vbell" ~/.screenrc || echo "vbell off" >> ~/.screenrc
echo "Configuring Profile"
grep -q "~/.workstation" ~/.bashrc || echo -e "\n[ -f ~/.workstation ] && . ~/.workstation" >> ~/.bashrc
echo "$(git config --global user.name) configured"
echo -en "see:\n\tworkstation help\n\n"
}
# Reloads bash
_workstation_reload()
{
clear
# unset other_function_list
# unset original_function_list
# unset misc_function_list
# unset workstation_function_list
source ~/.workstation
}
# Updates workstation from github
_workstation_pull()
{
if [ -d ~/.workstation.git ]
then
cd ~/.workstation.git
git pull
cd -
else
# TODO: dynamic url
# Check keys ok before mving!
git clone git@gist.github.com:/5719199.git ~/.workstation.git
mv ~/.workstation ~/.workstation.bak
ln -s ~/.workstation.git/.workstation.sh ~/.workstation
fi
workstation reload
}
# push workstation to github
_workstation_push()
{
if [ -d ~/.workstation.git ]
then
cd ~/.workstation.git
git commit -am "updated with workstation push"
git push
cd -
else
echo "no local workstation repo, use 'workstation pull' first"
fi
}
# Provides various helpful functions
workstation()
{
# If no member function passed, show help
if [ -z "$1" ]
then
workstation help
return 0
fi
local member_function="$(printf "_%s_%s" "$FUNCNAME" "$1")"
if [ "$(type -t "$member_function")" = "function" ]
then
$member_function
else
echo -en "Undefined function: $member_function\nsee:\n\tworkstation help\n\n"
return 1
fi
}
# Enable running workstation functions by either:
# - workstation <function name>
# - _workstation_<function name>
# - . ~/.workstation <function name>
[ -n "$1" ] && workstation $1
# Get list of workstation functions
[[ "$workstation_function_list" ]] ||
workstation_function_list=$(grep -Fxv -f \
<(echo -e "$original_function_list\n$misc_function_list\n$other_function_list") \
<(compgen -A function))
# Provide autocompletions
workstation_function_names="$(echo $workstation_function_list | sed 's/_workstation_//g')"
workstation_function_names="$(echo $workstation_function_names | sed 's/ workstation//g')"
complete -W "$workstation_function_names" workstation
# Ensure SSH agent is running
# eval $(ssh-agent) > /dev/null 2>&1 &
# ssh-add ~/.ssh/*
## TODO ##
# Generate Key
# SSH Agent
# Fix the repeat function
# http://ithaca.arpinum.org/2013/01/02/git-prompt.html
# Disk / RAM / CPU Alerts on login
# Change all the helper functions to use gist and have the code separate
# Separate Desktop vs Server programs
## NOTES ##
# Bash Manual: http://linux.die.net/man/1/bash
# Dev Setup: https://gist.github.com/deanrather/4327301
# Sublime Setup: https://gist.github.com/deanrather/2885590
# Keyboard Shortcuts: https://gist.github.com/deanrather/2915320
# Using Vim: https://gist.github.com/deanrather/7310797
# Using Git: https://gist.github.com/deanrather/5572701
# Using VimDiff: https://gist.github.com/mattratleph/4026987
#!/bin/bash
# Workstation
alias ws="workstation"
alias wr="workstation reload"
alias wp="workstation push && workstation pull"
# Vagrant
alias v="vagrant"
alias vup="vagrant up"
alias vssh="vagrant ssh"
alias vs="vagrant status"
# Git
alias g="git"
alias gp="git pull --no-edit && git push"
alias gs="git status"
alias gd="git diff"
alias gf="git fetch && git fetch --tags"
alias gb="git_branch"
alias ga="git add ."
alias gc="git commit -am"
alias ta="tig --all"
alias gsu="git submodule update --init --recursive"
# Edit
alias edit="vim"
alias e="edit"
alias ea="edit ~/.workstation.d/aliases.sh && wr"
alias eg="edit ~/.gitrc"
alias et="edit ~/.tmux.conf"
alias ev="edit ~/.vimrc"
alias ew="edit ~/.workstation.git/.workstation.sh"
# Other
alias i="sudo apt-get install -y"
alias locate="sudo updatedb; locate"
alias explorer="nautilus"
# tmux.conf
# -*- mode: yaml -*-
#
# Installation:
#
# wget -O ~/.tmux.conf deanrather.github.io/dotfiles/tmux.conf
#
# Note:
# - TMUX prevents highlight-to-copy, so use shift+highlight-to-copy.
# - `bind -n` binds without prefix. Without this you have to press <prefix> before the keys
# - `bind -r` allows you to press prefix once, then other keys in sequence
# - can't bind over the top of OS binds. eg: `C-pageDown` is taken by gnome-terminal.
#
# Aliases:
# - `bind-key` == `bind`
# - `C-<key>` == Ctrl+<key>
# - `S-<key>` == Shift+<key>
## Keybinds
# Clear existing keybinds
unbind-key -a
# Use (Ctrl-a) as the prefix (like in gnu-screen)
set-option -g prefix C-a
bind C-a send-prefix
# Reload config file with (prefix r)
bind r source-file ~/.tmux.conf \; display '~/.tmux.conf reloaded!'
# Scrolling with (pageUp) and (pageDown)
# bind -n pageUp copy-mode -u
# bind -n pageDown copy-mode -d
# Open command prompt with (prefix :)
bind : command-prompt
## Windows Keybinds
# New Window with (Ctrl+n)
bind -n C-n new-window
# Close Window
# it's Ctrl+d
# Prev window with (Ctrl+home)
# Next window with (Ctrl+end)
bind -n C-home prev
bind -n C-end next
# Swap window with prev (prefix home)
# Swap window with next (prefix end)
bind home swap-window -t -1
bind end swap-window -t +1
# Rename window with (F2)
# and don't keep old name
bind -n F2 command-prompt -p (rename-window) "rename-window '%%'"
## Panes Keybinds
# Split horizontally with (prefix h)
# Split vertically with (prefix v)
bind h split-window -h
bind v split-window -v
# Switch Pane with (prefix up|down|left|right)
bind up select-pane -U
bind down select-pane -D
bind left select-pane -L
bind right select-pane -R
## Settings
# start with window 1 (instead of 0)
set -g base-index 1
# when closing a window, renumber higher ones to fill the gap
set -g renumber-windows on
# start with pane 1
set -g pane-base-index 1
# use two hundred and fifty-six colours!
set -g default-terminal "screen-256color"
# increase history limit
set -g history-limit 10000
# allow terminal scrolling
set-option -g terminal-overrides 'xterm*:smcup@:rmcup@'
# use mouse in copy mode
setw -g mode-mouse on
# select window with clicking
set -g mouse-select-window on
# panes styling
set -g pane-border-fg colour0
set -g pane-active-border-fg colour0
# select pane with mouse click
set -g mouse-select-pane on
# resize panes with mouse drag on borders
set -g mouse-resize-pane on
# make ctrl+left, right jump left-right works as per usual
set-window-option -g xterm-keys on
## shortcuts
# open a man page in new window with (prefix ?)
bind ? command-prompt "split-window 'exec man %%'"
# quick view of processes with (prefix ~)
bind '~' split-window "exec htop"
# Styling
# windows status
set-option -g status-position bottom
setw -g window-status-format "#[bg=colour241,fg=colour0,noreverse]█▓░ #W "
setw -g window-status-current-format "#[bg=colour66,fg=colour0,noreverse]█▓░ #W "
# status line
set -g status-utf8 on
set -g status-justify left
set -g status-bg colour0
set -g status-fg colour66
set -g status-interval 2
# messaging
set -g message-fg colour0
set -g message-bg colour66
set -g message-command-fg colour66
set -g message-command-bg colour1
# status bar - window id, pane id, :: date
set -g status-right-length 60
set -g status-right "#[fg=blue]#I:#P #[fg=green]|%l:%M %p"
#window mode
setw -g mode-bg colour66
setw -g mode-fg colour0
# Info on left (no session display)
set -g status-left ''
## Unsure if I like settings
# vim style copy paste mode
unbind [
bind Escape copy-mode
unbind p
bind p paste-buffer
bind -t vi-copy 'v' begin-selection
bind -t vi-copy y copy-pipe 'xclip -in -selection clipboard'
# use vi mode
setw -g mode-keys vi
set -g status-keys vi
setw -g utf8 on
#resizing
setw -g aggressive-resize on
# loud or quiet?
set-option -g visual-activity on
set-option -g visual-bell off
set-option -g visual-silence off
set-window-option -g monitor-activity off
set-option -g bell-action none
@deanrather
Copy link
Author

todo: user bash_profile instead of bashrc
it only loads once on login, not on each terminal open
also sort out workstation!

@deanrather
Copy link
Author

https://github.com/jarun/google-cli
todo: use git.io/deanrather/dotfiles instead

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