Improving command line efficiency via Bash history

If you work in Bash with any frequency, hopefully you collect aliases to make your life easier. Complicated and highly useful commands should be saved and re-used so that you can get to the information you need in the least possible time. However, some longish commands might slip by your notice and not get changed into aliases.

In that spirit, I have created a function to provide better visibility into your current Bash history. It prints out the current top ten most common commands in your history and how often each has been used. This in itself is pretty common, and often implemented via something like this.

My script also adds the percentage of each count out of your total history and statistics like date range. It also prints commands in red if they are 10 characters or more in length. This was an arbitrary selection, adjust as you see fit. The basic idea is that when you run it, you can easily see if you have been running longish commands rather often, providing you with excellent candidates for alias formation.

Important caveat: This function pulls out various columns of history’s output for processing and presentation. These are entirely dependent on the format of your history. My history is of the form:

1
HISTORY_NUMBER YYYY-MM-DD HH:MM:SS - COMMAND

which I find most useful. How to set this up is described in this post. If you have your history set up differently (and decide not to switch to my lovely format:-)), please adjust the columns as needed. It should be pretty easy to figure out. Current code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function cmds () {
    # This lets us iterate per line instead of over count then cmd:
    export IFS=$'\n'
    export HISTORY_LENGTH=`history | wc -l`
    export TOP_TEN_CMDS=`history | awk '{print $5}' | sort | uniq -c | sort -n | tail -n 10`
    export START_DATE=`history | awk '{print $2}' | sort | uniq | sort -r | tail -n 1`
    export END_DATE=`history | awk '{print $2}' | sort | uniq | sort | tail -n 1`
    echo -e "\033[34mTop ten most used commands (Count, Command, % of history):\033[0m "
    # Show the 10 most common commands, with percentage of total, in red if 10 chars or more.
    for CMD in $TOP_TEN_CMDS; do
        export CMD_COUNT=`echo $CMD | awk '{print $1}'`
        export CMD_PERCENT=`echo "scale=4; $CMD_COUNT / $HISTORY_LENGTH" | bc`
        export SHORT_CMD=`echo $CMD | awk '{print $2}'`
        if [ ${#SHORT_CMD} -ge 10 ]; then
            echo -e "\033[31m * $CMD ($CMD_PERCENT%)\033[0m - Length ${#SHORT_CMD}, might want to form an alias for this."
        else
            echo " * $CMD ($CMD_PERCENT%)"
        fi
    done
    echo -e "\033[34m$HISTORY_LENGTH lines in history from $START_DATE to $END_DATE.\033[0m"
    unset IFS
}

This will produce output (depending on your history format, see caveat above) like:

Side note: I tried moving this into python once it began to get a little complicated. The problem I ran into was that history isn’t a command, it’s a bash shell builtin! Thus, when I tried things like:

1
2
3
4
import os, subprocess, commands
os.system('history')
subprocess.call('history')
commands.getoutput('history')

I got back “command not found” and the like. I currently don’t know how to call bash shell builtins from Python, so if anyone has any insight, please share!

Post to Twitter Post to Delicious Post to Digg Post to Reddit

No related posts.

Related posts brought to you by Yet Another Related Posts Plugin.

This entry was posted in CLI, Programming and tagged , , , . Bookmark the permalink.

6 Responses to Improving command line efficiency via Bash history

  1. anonim says:

    AFAIK history just prints the contents of ~/.bash_history and it’s line number, so you could write that in python, a very quick and dirty solution:

    import os

    def do_history():
    h = open(‘%s/.bash_history’ % os.environ['HOME'])
    counter = 0
    for line in h.readlines():
    counter += 1
    print counter, #!/usr/bin/python
    import os

    def do_history():
    h = open(‘%s/.bash_history’ % os.environ['HOME'])
    counter = 0
    for line in h.readlines():
    counter += 1
    print counter, line.replace(‘\n’,”)

    if __name__ == ‘__main__’:
    do_history()

  2. anonim says:

    Sorry but I managed to blow the post, here we go again:

    import os

    def do_history():
    h = open(‘%s/.bash_history’ % os.environ['HOME'])
    counter = 0
    for line in h.readlines():
    counter += 1
    print counter, line.replace(‘\n’,”)

    if __name__ == ‘__main__’:
    do_history()

  3. I wanted to steer clear of just using .bash_history because that doesn’t take into account the format and other options declared in .bashrc and .bash_profile, as the history builtin does. So if you filter out certain commands, or do anything else custom, the file won’t reflect that, which seemed sub-optimal to me.

  4. Hugo Heden says:

    Lovely!

    Rookie question, sorry:

    Do you suggest to put that function in ~/.bashrc ? Or rather as a separate script in ~/bin ?

    And the what about the *alias* commands?

  5. @Hugo: I am on the fence about where to put this function. Right now I keep it in my ~/.bashrc. If it were any longer, I would likely pull it out into my directories of personal scripts, symlinked somewhere system-appropriate.

    The alias commands I would definitely keep in ~/.bashrc, so that all my defined aliases are in one intuitive place.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>