Browse Tag: stupid bash tricks

long-running bash command notifier for osx

I stumbled across this fantastic blog post that offers a clever bash script to notify you of the completion of long-running commands in your bash shell. I made a couple tweaks to make it work for OSX, and gave it a little blacklist (I usually run `less’ or `vim’ for >10 seconds, for example).

Requires growl and growlnotify, bash, and this clever pre-exec hook for bash. Download that pre-exec hook:

mkdir -p ~/src/shell-tools
curl http://www.twistedmatrix.com/users/glyph/preexec.bash.txt > ~/src/shell-tools/preexec.bash

Now copy and paste this into ~/src/shell-tools/long-running.bash:

# Source this, and then run notify_when_long_running_commands_finish_install
#
# Relies on http://www.twistedmatrix.com/users/glyph/preexec.bash.txt
# Full credit to http://code.mumak.net/2012/01/undistract-me.html
# Modified slightly for OSX support and blacklist (see the egrep loop in the
# precmd() function

if [ -f ~/src/shell-tools/preexec.bash ]; then
    . ~/src/shell-tools/preexec.bash
else
    echo "Could not find preexec.bash"
fi

LONG_RUNNING_COMMAND_TIMEOUT=10

function notify_when_long_running_commands_finish_install() {
    local RUNNING_COMMANDS_DIR=~/.cache/running-commands
    mkdir -p $RUNNING_COMMANDS_DIR
    for pid_file in $RUNNING_COMMANDS_DIR/*; do
        local pid=$(basename $pid_file)
        # If $pid is numeric, then check for a running bash process.
        case $pid in
        ''|*[!0-9]*) local numeric=0 ;;
        *) local numeric=1 ;;
        esac

        if [[ $numeric -eq 1 ]]; then
            local command=$(ps -o command= $pid)
            if [[ $command != $BASH ]]; then
                rm -f $pid_file
            fi
        fi
    done

    _LAST_COMMAND_STARTED_CACHE=$RUNNING_COMMANDS_DIR/$$

    function precmd () {

        if [[ -r $_LAST_COMMAND_STARTED_CACHE ]]; then

            local last_command_started=$(head -1 $_LAST_COMMAND_STARTED_CACHE)
            local last_command=$(tail -n +2 $_LAST_COMMAND_STARTED_CACHE)

            if [[ -n $last_command_started ]]; then
                local now=$(date -u +%s)
                local time_taken=$(( $now - $last_command_started ))
                if [[ $time_taken -gt $LONG_RUNNING_COMMAND_TIMEOUT ]]; then
                  if [ `echo "$last_command" | egrep -c "less|more|vi|vim|man|ssh"` == 1 ] ; then 
                    exit 0
                  else
                    growlnotify \
                        -m "$last_command completed in $time_taken seconds" \
                        "Command complete:"
                  fi
                fi
            fi
            # No command is running, so clear the cache.
            echo -n > $_LAST_COMMAND_STARTED_CACHE
        fi
    }

    function preexec () {
        date -u +%s > $_LAST_COMMAND_STARTED_CACHE
        echo "$1" >> $_LAST_COMMAND_STARTED_CACHE
    }

    preexec_install
}

Finally, source it by adding the following to your ~/.bash_profile:

. ~/src/shell-tools/preexec.bash
. ~/src/shell-tools/long-running.bash
notify_when_long_running_commands_finish_install

also: site redesign! (read: i installed a new theme from the gallery, go team)

Extend bash functionality

[code lang=”bash”]# make bash autocomplete with up arrow
bind ‘”\e[A”:history-search-backward’
bind ‘”\e[B”:history-search-forward’

# make tab cycle through commands instead of listing
bind ‘”\t”:menu-complete'[/code]

Intercepting “command not found” in bash

On Debian, bash is patched with an interesting new function: command_not_found_handle. This intercepts exit code 127 (“command not found”) and allows you to do neat things. Debian uses it to pass it through the apt database, letting you know if a command you tried to invoke is not available, but can be found in the apt repos, and how to install it. Pretty spiffy.

This, of course, can be modified. Where I work, we use numbers to identify servers. I have a script that grabs login credentials from our internal systems and auto-logs me into servers based on their number. For example, I’d run `connect 12345′ to connect to server 12345.

By adding the following to my .bashrc:

[code lang=”bash”]function command_not_found_handle {
/home/kale/bin/command-not-found $1
}
[/code]

And creating the following script, with regex in place to only care about numbers, placed in /home/kale/bin/command-not-found:

[code lang=”bash”]#!/bin/bash

MYBOOL=`echo $1 | awk ‘$1 ~ /^[0-9]+-*[0-9]*$/ {print $0}’ | wc -l | awk ‘{print $1}’`

if [ “$MYBOOL” == “1” ]; then
/home/kale/bin/connect $1
else
exit 127
fi
[/code]

(where `connect’ is the path to my connect script, previously written)

This now allows me to do this awesome deal:

[code lang=”bash”]kale@bastion:~$ 12345
Connecting to server 12345…
root@12345:~#
[/code]

If you didn’t catch it, I don’t need to specify a command — just the argument. As there’s no application in my $PATH named `12345′, it falls through to the command_not_found_handle function, which then launches my connect script.

Who needs commands? I just saved hundreds of wasted seconds per night on typing “connect”!

Bash portscanner

Well, why not?

[code lang=”bash”]HOST=127.0.0.1;for((port=1;port<=65535;++port));do echo -en "$port ";if echo -en "open $HOST $port\nlogout\quit" | telnet 2>/dev/null | grep ‘Connected to’ > /dev/null;then echo -en “\n\nport $port/tcp is open\n\n”;fi;done[/code]