Jump to: navigation, search

GeekTool is a great application for OS X that allows you to display the contents of shell scripts, images, and files directly on the desktop. This page provides some scripts that I have written or adapted from others I've found to provide useful information on the desktop with GeekTool.

I put all of my scripts into ~/bin/gt-scripts/ to keep them all organized and together.


The following are scripts that I use with GeekTool. They were either written by me or were found elsewhere and adapted/improved or otherwise changed to suit my needs.

IP Information

This script displays your current local and external IP addresses.



external=$(curl -s | awk {'print $1'})
internal0=$(ifconfig en0 | grep "inet" | grep -v | grep -v inet6 | cut -d ' ' -f 2)
internal1=$(ifconfig en1 | grep "inet" | grep -v | grep -v inet6 | cut -d ' ' -f 2)

echo "External : ${external}"

if [ "${internal0}" != "" ]; then
    echo "Ethernet : ${internal0}"

if [ "${internal1}" != "" ]; then
    echo "Ethernet : ${internal1}"


This script displays the calendar for this month with the current day hashed out.


cal | sed "s/^/ /;s/$/ /;s/ $(date +%e) / $(date +%e | sed 's/./#/g') /"

Uptime, CPU/RAM usage

This script displays current uptime as well as RAM and CPU usage on the system.



# in 10.5 use the following:
#cpu=$(top -l 1| grep 'Load Avg' | awk '{print $12, $13}')
# in 10.6 use the following:
cpu=$(top -l 1|grep 'CPU usage' | awk '{print $7, $8}')
inactivemem=$(top -l 1 | awk '/PhysMem/ {print $6}')
freemem=$(top -l 1 | awk '/PhysMem/ {print $10}')

if [ "$(echo ${inactivemem} | egrep '.*G$')" ]; then
    inactivemem=$(echo ${inactivemem} | cut -d 'G' -f 1)
    let inactivemem=${inactivemem}*1024
    inactivemem=$(echo ${inactivemem} | cut -d 'M' -f 1)

if [ "$(echo ${freemem} | egrep '.*G$')" ]; then
    freemem=$(echo ${freemem} | cut -d 'G' -f 1)
    let freemem=${freemem}*1024
    freemem=$(echo ${freemem} | cut -d 'M' -f 1)

let freemem=${freemem}+${inactivemem}

freemem=$(echo "scale=2; ${freemem} / 1024" | bc)
uptime=$(uptime | awk '{print $3 " " $4 " " $5 }' | sed -e 's/.$//g')

echo "Uptime : ${uptime}"
echo "CPU    : ${cpu}"
echo "RAM    : ${freemem}G"

Disk Usage

This script shows current mounted volumes and disk usage, separating local and remote volumes:



export LC_ALL=en_US.UTF-8
export LC_CTYPE=C

local=$(df -hl | egrep -v '(devfs|fdesc)')
remote=$(df -h | egrep -v '(devfs|fdesc|/dev/disk|map)')

echo "Local filesystems:\n\n${local}\n"
if [ "$(echo ${remote} | grep Volumes)" != "" ]; then
    echo "Remote filesystems:\n\n ${remote}\n"


This script displays current weather conditions. You will need to change the p= value in the URL to your own location code or you'll see what crappy weather we currently have in Edmonton.


weather=$(curl --silent "" | grep -E '(Current Conditions:|C<BR)' | \
  sed -e 's/Current Conditions://' -e 's/<br \/>//' -e 's/<b>//' -e 's/<\/b>//' -e 's/<BR \/>//' -e 's/<description>//' -e 's/<\/description>//')
# doing it this way gets rid of a preceeding blank line
echo ${weather}

The Hit List

I really like Potion Factory's The Hit List but only found a script written in Ruby that would let me get the information I wanted. I'm not 100% satisfied with this script so it will likely be updated in the future yet, but this two-script combo works pretty well for my needs right now. It is a combination of Python and AppleScript.. AppleScript gets the data from The Hit List and Python calls the AppleScript and does all the formatting of the output. This way I can have my upcoming list pretty much in my face all the time.


tell application "System Events" to if (name of every process) contains "The Hit List" then
    set theList to ""
    set lf to (ASCII character 10)
    tell application "The Hit List"
        set upTasks to every task in upcoming list
        set todayTasks to every task in today list
        set allTasks to upTasks & todayTasks
        repeat with aTask in allTasks
            if completed of aTask is true then
                set checkbox to "/"
            else if canceled of aTask is true then
                set checkbox to "x"
                set checkbox to "-"
            end if
            set taskText to title of aTask
            set taskStart to start date of aTask
            set taskDue to due date of aTask
            set theList to theList & checkbox & " |" & taskText & "|" & taskStart & "|" & taskDue & lf
        end repeat
    end tell
    return theList
end if

#!/usr/bin/env python

import os, commands, re, calendar
from datetime import date

re_tag     = re.compile(r'@\S+')
re_context = re.compile(r'/\S+')

def print_item(todo):
    item = re_tag.sub(' ', todo['item'])  # not working

    if todo['start'] == '0':
        start  = 'TODAY!'
        prefix = '!!!'
    elif todo['start'] == '1':
        start  = 'Tomorrow'
        prefix = '***'
        start  = 'in %s days' % todo['start']
        prefix = '  +'

    pdue = '  '
    if todo['due'] == '0':
        due    = '   *** TASK IS DUE TODAY! ***'
        pdue   = '!!'
    elif todo['due'] == '1':
        due    = '   Due: Tomorrow'
    elif todo['due'] == '-1':
        due    = ''
        due    = '   Due: in %s days' % todo['due']

    print '%s%s %s' % (pdue, prefix, item)
    print '      Start: %s  %s' % (start, due)

def trans_date(input_date):
    #Saturday, August 15, 2009 12:00:00 AM
    if input_date == 'none' or input_date == 'missing value':
        return '-1'

# need to handle overdue tasks

    (dname, month, day, year, time, extra) = input_date.split(' ')
    dname = dname.split(',')[0]
    day   = day.split(',')[0]
    (hour, min, sec) = time.split(':')
    month = list(calendar.month_name).index(month)

    today   =
    tdate   = date(int(year), int(month), int(day))
    timedue = str(abs(tdate - today)).split(' ')[0]
    if timedue == '0:00:00':
        timedue = '0'

output = commands.getoutput("osascript /Users/vdanen/bin/gt-scripts/hitlist.scpt")
lines  = output.split('\n')

contexts = []
tags     = []
todos    = {}
count    = 0

for items in lines:
        (status, item, start, due) = items.split('|')

    if due == 'missing value':
        due = 'none'

    if status.strip() == '/' or status.strip() == 'x':
        # don't add completed or cancelled items

    todos[count]           = {}
    todos[count]['item']   = item.strip()
    todos[count]['status'] = status.strip()
    todos[count]['start']  = trans_date(start.strip())
    todos[count]['due']    = trans_date(due.strip())

    p_ctxt = item.split(' ')
    for x in p_ctxt:
        # regexp for '@+' and '/+'
            x = x.split('@')[1]
            todos[count]['tags'] = []
            if not x in tags:

            x = x.split('/')[1]
            todos[count]['contexts'] = []
            if not x in contexts:

    count += 1

notags = []

# slight bug here: if an item has more than one tag, it will
# go in only one list, not both
print 'Upcoming The Hit List items'

# also need to sort by due/start dates
for t in tags:
    count = []

    for x in todos:
        if 'tags' in todos[x]:
            for ptag in todos[x]['tags']:
                if ptag == t:
            if not x in notags:

    if len(count) > 0:
        print '\nTODO items tagged "%s"\n' % t
        for x in count:

if len(notags) >= 1:
    print '\nTODO items with no tags\n'
    for x in notags:

Revision History

  • 09/01/2009 - fix the script due to output errors from 10.6
  • 08/30/2009 - fix the gt-cpustats script with info for 10.6
  • 08/14/2009 - initial article