dot files and the search for the perfect bash setup



the prompt

What makes a good bash prompt?

Ubuntu 17.10 has a nice easy to look at prompt, but it doesn’t give you a lot of information.

acodeninja@laptop:~$ _

It’s nice to have some information relevant to your current task.

[acodeninja@laptop] [master !? - me@acode.ninja] 
~/code/dotty > _

Or tasks you have running in the background

[acodeninja@laptop] [jobs: 1 running 2 stopped] 
~/code/dotty > _

Or the exit code of your last command

[acodeninja@laptop] [130] 
~/code/dotty > _

The following functions give us all we need to put the information above into our terminal prompt.

#!/bin/sh

# get last command return value
function nonzero_return() {
	RETVAL=$?
	[ $RETVAL -ne 0 ] && echo " [$RETVAL]"
}

# get the current git repo author email
function get_git_author() {
	BRANCH=`git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/'`
	if [ ! "${BRANCH}" == "" ]
	then
		echo " [$(git config user.email)]"
	else
		echo ""
	fi
}

# get current branch in git repo
function get_git_branch() {
	BRANCH=`git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/'`
	if [ ! "${BRANCH}" == "" ]
	then
		STAT=`get_git_dirty`
		echo " [${BRANCH}${STAT}]"
	else
		echo ""
	fi
}

# get current status of git repo
function get_git_dirty {
	status=`git status 2>&1 | tee`
	dirty=`echo -n "${status}" 2> /dev/null | grep "modified:" &> /dev/null; echo "$?"`
	untracked=`echo -n "${status}" 2> /dev/null | grep "Untracked files" &> /dev/null; echo "$?"`
	ahead=`echo -n "${status}" 2> /dev/null | grep "Your branch is ahead of" &> /dev/null; echo "$?"`
	newfile=`echo -n "${status}" 2> /dev/null | grep "new file:" &> /dev/null; echo "$?"`
	renamed=`echo -n "${status}" 2> /dev/null | grep "renamed:" &> /dev/null; echo "$?"`
	deleted=`echo -n "${status}" 2> /dev/null | grep "deleted:" &> /dev/null; echo "$?"`
	bits=''
	if [ "${renamed}" == "0" ]; then
		bits=">${bits}"
	fi
	if [ "${ahead}" == "0" ]; then
		bits="*${bits}"
	fi
	if [ "${newfile}" == "0" ]; then
		bits="+${bits}"
	fi
	if [ "${untracked}" == "0" ]; then
		bits="?${bits}"
	fi
	if [ "${deleted}" == "0" ]; then
		bits="x${bits}"
	fi
	if [ "${dirty}" == "0" ]; then
		bits="!${bits}"
	fi
	if [ ! "${bits}" == "" ]; then
		echo " ${bits}"
	else
		echo ""
	fi
}

# Get count of background processes
function get_background_job_count {
	STOPPED_FILE=/tmp/$$-stopped
	RUNNING_FILE=/tmp/$$-running
	echo 0 > $STOPPED_FILE
	echo 0 > $RUNNING_FILE

	jobs | while read line ; do
		if [[ $line =~ "Stopped" ]]; then
			STOPPED=$(($(cat $STOPPED_FILE) + 1))
			echo $STOPPED > $STOPPED_FILE;
		fi
		if [[ $line =~ "Running" ]]; then
			RUNNING=$(($(cat $RUNNING_FILE) + 1))
			echo $RUNNING > $RUNNING_FILE;
		fi
	done

	STOPPED=$(cat $STOPPED_FILE)
	RUNNING=$(cat $RUNNING_FILE)

	if [ "$STOPPED" != "0" ] || [ "$RUNNING" != "0" ]; then
		echo " [$RUNNING running $STOPPED stopped]";
 	fi
}

function get_load_average {
	echo " [load: $(cat /proc/loadavg)]"
}

function get_current_versions {
	BITS=""

	if [ "$(which php)" != "" ]; then
		if [ "$BITS" != "" ]; then
			BITS="$BITS ";
		fi
		BITS="${BITS}php: v$(echo "<?php \$matches = []; preg_match('/([0-9\.]+)/', PHP_VERSION, \$matches); echo \$matches[0];" | php)"
	fi

	if [ "$(which node)" != "" ]; then
		if [ "$BITS" != "" ]; then
			BITS="$BITS ";
		fi
		BITS="${BITS}node: $(node -v)"
	fi

	if [ "$BITS" != "" ]; then
		echo " [$BITS]"
	fi
}

We can then inject the information into our prompt with the following:

!/usr/bin/env bash
THIS_DIR=$( cd $( dirname ${BASH_SOURCE[0]} ) >/dev/null 2>&1 && pwd )

source $THIS_DIR/functions.sh

function generateps1 { 
    local CURRENTTERM_WIDTH="$(tput cols)"
    
    local COLOUR_GRAY="\033[1;30m"
    local COLOUR_LIGHT_GRAY="\033[0;37m"
    local COLOUR_CYAN="\033[0;36m"
    local COLOUR_RED="\033[0;31m"
    local COLOUR_YELLOW="\033[0;33m"
    local COLOUR_ORANGE="\033[1;31m"
    local COLOUR_NONE="\033[0m"
    
    local LAST_EXIT_CODE=$(nonzero_return)
    
    local USERNAME=$(whoami)
    local HOSTNAME=$(hostname)
    local GIT_CURRENT_BRANCH=$(get_git_branch)
    local GIT_CURRENT_AUTHOR=$(get_git_author)
    local BACKGROUND_JOBS=$(get_background_job_count)
    local GET_CLI_VERSIONS=$(get_current_versions)
    
    local TOP_LINE="\n$COLOUR_LIGHT_GRAY[$USERNAME@$HOSTNAME]$COLOUR_NONE"
          TOP_LINE="$TOP_LINE$COLOUR_CYAN$GIT_CURRENT_AUTHOR$COLOUR_NONE"
          TOP_LINE="$TOP_LINE$COLOUR_RED$BACKGROUND_JOBS$COLOUR_NONE"
    
    if [ "$(tput cols)" -gt "100" ]; then
        TOP_LINE="$TOP_LINE$GET_CLI_VERSIONS"
    fi
    
    local PROMPT="$(dirs)$COLOUR_YELLOW$GIT_CURRENT_BRANCH$COLOUR_NONE > "
    
    echo -e "$TOP_LINE\n$PROMPT"
}

export PS1="`generate_ps1`"

Using the above we get a really nice looking prompt with all the information you could want at your fingertips.

![Nice looking prompt you have there]({{ “/assets/images/posts/2018-03-12/ps1-prompt.png” | absolute_url }})

the aliases

Ubuntu includes some nice default aliases ll is an alias of ls -al and can have more arguments added to it ll -h works perfectly well.

Here are some I use.

colour grep output for ease of use

alias grep='grep --color=auto' 
alias egrep='egrep --color=auto' 
alias fgrep='fgrep --color=auto'

confirm file operations

alias mv='mv -i' 
alias cp='cp -i' 
alias ln='ln -i'
alias rm='rm -i'

handy ls aliases

alias ll='ls -al' 
alias l='ls -l' 
alias la='ls -a'

handy php version switcher

function phpswitch { 
  if [ "$(which php$1)" != "" ]; then
    alias php="php$1"
    echo "Switched to php$1 successfully"
  else
    echo "Could not find php$1"
  fi 
}

All of this and more is at @acodeninja/.dotty , my collection of bash terminal customisations geared toward developers working with linux.