A shell-centric view of the world

If you spend a lot of time at the computer and learn to watch yourself as you work, you often find yourself repeating sequences of operations and looking for ways to automate them. As a result one's interface to the computer grows organically over the years around one's mental habits and personal idiosyncracies. Some scalings from my shell (zsh or bash):

  • Just because I am a CLI bigot doesn't mean I am entirely closed to the possibilities of a GUI environment. Ever have a bunch of data in a file that you wished you could just 'see'? Wouldn't it be nice, for example, to be able to say:
    $ grep "^Date: " mail |column 3 |freq |plot with lines
    
    and have it pop up a graph0 in a new window? Here's an implementation for plot:1
    plot () {
        ( echo "plot \"-\"" "$@" ; cat ) |gnuplot -persist
    }
    
    A nice bonus: plot passes its arguments straight to gnuplot's plot command. Want bargraphs?
    plot with impulses
    
    Ugly, but functional.

  • Taking the idea further, consider Pipe Viewer - a clone for cat that shows a text progress bar as it works. Really nice, except it can munge an app's stderr stream. Wouldn't it be much nicer to have it pop up a progress bar2 instead? Armed with progress we can now say things like:
    gpv () {
        { pv -f "$@" 3>&2 2>&1 1>&3 |progress "$@" ; } 2>&1
    }
    
    So this pipe will pop up a progress bar as gnuplot crunches through a large data file:
    $ gpv data_file |column 4 |normalize |cumulative |plot with lines
    
    Being able to create and manipulate GUI widgets in shell scripts opens up a whole new modality for them.

  • The plot command above can be thought to be obtained by taking a command under a subordinate shell - gnuplot - and 'exporting' it to the top-level shell. This happens to me all the time: I get used to typing a command in a subordinate shell, start typing it in my shell by mistake, and eventually go, "Aha!" A case in point: when I started using p[rint] in gdb as my calculator,3 it was only a matter of time before I wanted to have the same command in my shell.
    p () {
        echo "$@" |bc -l
    }
    

  • Google for my email? I'd rather actively delete junk so I can grep4 without having to index:
    mgrep () {
        agrep -id '^From ' "$1" $MAILDIR/* > $MAILDIR/x
        pine -i -f x
        rm $MAILDIR/x
    }
    

  • Photo albums with thumbnails in a dozen lines:5
    album () {
        ls *.{jpg,JPG} > index.html 2>/dev/null
    
        # Thumbnails.
        for i in `cat index.html`
        do
            djpeg $i |pnmscale -height 300 \
                |cjpeg > `echo "$i" |perl -pwe 's/jpg$|JPG$/t.jpg'`
        done
    
        cat > x << EOF
            %s/.*/& &/
            %s/\.jpg$/.t.jpg/i
            %s/jpg /jpg"><img src="/i
            %s/^/<p><a href="/
            %s/$/"><\/a>/
        EOF
        vim -c "so x |wq" index.html
    }
    
    If you think you need to split the generated html into multiple files you probably have too many pictures for your audience's liking.

  • One of my oldest and most-used scripts lets me easily specify arbitrary paths to cd into:
    $ pwd
    /some/long/dir/ect/ory/str/uct/ure
    $ cd arch
    /another/inode/deep/down/the/fs/hier/archy
    $ cd herfs arch
    /yet/anoth/er/inode/deep/down/the/otherfs/hie/rarchy
    
    Much more general than CDPATH. It works by picking the first acceptable directory in ~/.dirlist:
    cd () {
        command cd "$@" 2>/dev/null && return
        ARG=`echo "$@" |perl -pwe 's# +#.*\/.*#g'`
        DIR=`grep $ARG ~/.dirlist`
        [ "$DIR" ] && command cd $DIR
    }
    
    There are a variety of ways to maintain ~/.dirlist, ranging from cron jobs that periodically run:
    find ~ -type d > ~/.dirlist
    
    to overloading mkdir, rmdir, rm, cp, mv.. Crucial decisions are what portions of the file system to index, and how to order paths with common names like in the above example.

  • Easy access to shell color escape codes. Example usage:
    echo `color red`foo`color reset`
    

  • ...

  • footnotes

    0. This graph plots by date the #mails I sent/received for the month of November 2004. Notice the spike right before the presidential elections.
    1. The other commands in that pipe are simple stubs in perl. I have a lot of those.
    2. Requires python and wish.
    3. A habit I picked up from Karu.
    4. agrep is a better grep.
    5. Requires netpbm and vim.