What are bash return codes > 128?

I like to keep PS1 pretty simple. I like to see the host I’m working on, the directory I’m in, the user I’m currently logged in as and the return code from teh last command I executed. This is easy to cobble together with bash escape sequences:

PS1=’\n[\u@\h][RC:$?][\w]$ ‘

The other day I was testing some new ansible playbooks and saw a return code of 130. Several years ago I read learning the bash shell and recalled something about the magic number 128. A quick search of the bash man page provided this gem:

“The return value of a simple command is its exit status, or 128+n if the command is terminated by signal n”

So a return code of 130 represents a process that is terminated by signal 2 which `kill -l` lists as SIGINT:

$ kill -l
 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP
21) SIGTTIN	22) SIGTTOU	23) SIGURG	24) SIGXCPU	25) SIGXFSZ
26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGIO	30) SIGPWR
31) SIGSYS	34) SIGRTMIN	35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
63) SIGRTMAX-1	64) SIGRTMAX	

Noting this here so I have it handy for the future.

Having fun in the shell with cowsay and fortune

Last weekend while I was waiting for several ansible playbooks to apply I thought it would be fun to play around with cowsay and fortune. If you aren’t familiar with these tools cowsay gives you an ASCII cow which says whatever is passed to it as an argument. Here is a example:

$ fortune | cowsay
 ________________________________
< Tomorrow, you can be anywhere. >
 --------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

To have a cowtastic time each time I open a shell I added the following to my bashrc:

# Have some fun
if [ -x /bin/cowsay ] && [ -x /bin/fortune ] || 
   [ -x /usr/games/cowsay ] && [ /usr/games/fortune ]; then
	   fortune | cowsay
fi

I guess the old saying is right. All work and no play makes an admin mooooooooo. :)

Automatically updating your .bashrc when you log into a server

I am a long time bash user and have found numerous aliases and shell functions that allow me to be more productive at the prompt. Depending on how you manage (configuration management, NFS mounted home directories, etc.) ${HOME} making sure your bashrc gets updated when you find a cool new feature can be a pain. I was tinkering around last weekend and thought about adding a block of code to my bashrc to run curl to grab the latest version of my bashrc from github. The following short code block works for my needs:

# Location to pull bashrc from
bashrc_source="https://raw.githubusercontent.com/Matty9191/bashrc/master/bashrc"

# Take precaution when playing with temp files
temp_file=$(mktemp /tmp/tmp.XXXXXXXX)
touch ${temp_file}

curl -s -o ${temp_file} ${bashrc_source}
RC=$?

if [ ${RC} -eq 0 ]; then
    version=$(head -1 ${temp_file} | awk -F'=' '/VERSION/ {print $2}')

    if [ "${version}" -gt "${VERSION}" ]; then
        echo "Upgrading bashrc from version ${VERSION} to ${version}"
        cp ${HOME}/.bashrc ${HOME}/.bashrc.bak.$(/bin/date "+%m%d%Y.%S")
        mv ${temp_file} ${HOME}/.bashrc
    fi
else
    echo "Unable to retrive bashrc from ${bashrc_source}"
    rm ${temp_file}
fi

If a new version is available (a VERSION variable tracks the release #) I get the following output when I log in:

Upgrading bashrc from version 44 to 46

If github is unavailable due to a service issue or a firewall won’t let me out the script will let me know:

Unable to retrieve bashrc from https://gik/Matty9191/bashrc/master/bashrc

How are you keeping your shell profiles up to date? Let me know in the comment section.

Fun times with the bash read function and subshells

There are a few shellisms that have bitten me over the years. One issue that has bitten me more than once is the interation of variable assignments when a pipe is used to pass data to a subshell. This annoyance can be easily illustrated with an example:

$ cat test

#!/bin/bash

grep MemTotal /proc/meminfo | read stat total size
echo $total

$ ./test

On first glance you would think that the echo statement would display the total amount of memory in the system. But alas, it produces nothing. The reason this occurs is because the grep output is piped to read which is run in a subshell. Read assigns the values passed from grep to the variables, but once read is finished the subshell it is running inside of will exit and the contents of stat, total and size will be lost.

To work around this we can implement one of the solutions proposed in the bash FAQ. Here is my favorite for cases similar to this:

$ cat test

#!/bin/bash

foo=$(grep MemTotal /proc/meminfo)
set -- $foo
total=$2
echo $total

$ ./test
8128692

This works because the output is stored in the variable foo and processed inside the current shell. No susbshells are created so there is no way for the variables to get nuked. If you haven’t read the bash FAQ, gotchas pages or Chris Johnson’s book you are definitely missing out. I still encounter goofy shell-related issues but am now able to immediately identify most of them since I’ve flooded myself with shell-related information. :) So what is your biggest annoyance with the various shells?

Converting time since the epoch to a human readable string

I was parsing some Netbackup logs today, and needed a way to convert the time since the epoch into a human readable string. A while back I read about the various forms of input that can be passed to the GNU date’s “-d” option, one of these being the time since the epoch:

$ date -d @1263408025
Wed Jan 13 13:40:25 EST 2010

This solved my issue, though unfortunately it’s not super portable. Food for thought.