...making Linux just a little more fun!

A Quick Lookup Script

By Ben Okopnik

Over the years of using computers, as well as in the process of growing older and more forgetful, I've discovered the value of having quick, focused ways of accessing important information. As an example, I have a password-protected, encrypted file containing my passwords and all the information related to them (i.e., hostname, username, security questions, etc.), as well as a script that allows me to look up the information and safely edit it; I also have a quotations file that I've been assembling for the last 20-plus years, and a script that looks up the quotations and lets me add new ones easily. In this article, I'll show yet another, similar script - one that lets me keep track of often-used commands and various tips and tricks that I use in Linux, Solaris, and so on.

For those familiar with database terminology, this is a minimal version of a CRUD (create, read, update, and delete) interface, minus the 'delete' option - as an old database administrator, I find even the thought of data deletion distasteful, anyway. :) Updating is a matter of adding a new entry with a distinct search string - and if you've added something that you decide you don't need later, all you have to do is not look it up.

Keeping shell scripts from blowing up

One of the things I've found out in writing and using this type of script is that there are pitfalls everywhere: any time you write something that takes human-readable information and tries to parse it, you're going to run into interface problems. Not only are the data items themselves suspect - although that's not an issue in this case, where the information is just a textual reminder - but also the user interaction with the script has edge cases all over the place. As a result, 99% of this script is tests for wrong actions taken by the user; the actual basic function of the script is only about a dozen lines. The most amusing part of how things can break is what you get after a while of not using the script and forgetting its quirks: even I, its author, have managed to crash it on occasion! These were always followed by half-amused, half-frustrated muttered curses and fixes - until there wasn't anything left to fix (at least, based on the problems that I could stumble across, find, imagine, or create.) As always, I'd appreciate feedback, if anyone discovers a problem I haven't found!

Usage

On my system, the script is simply called 'n' - as I recall, I was originally thinking of 'note' at the time that I named it. You can download the latest version from here, although the one on the LG server isn't likely to go stale any time soon; this script hasn't changed in quite a while.

The run options for this script are deceptively simple, and can be seen by invoking it with '-h' or '--help' as an argument:

ben@Jotunheim:~$ n -h

  n [-h|--help] | [section] [comment_search_string]
  
  If invoked without any argument, asks for the section and entry text to
  be added to the data file. If an argument is provided, and matches an
  existing section, that entire section is displayed; if two arguments
  are supplied, displays the first entry matching arg[2] in the section
  matching arg[1].

Here's an example of adding a new entry:

ben@Jotunheim:~$ n 
Current sections:
awk
date
find
grep
mencoder
misc/miscellany/miscellaneous/general
mount
mp3/ogg/wav
mplayer
netstat
perl
shell/bash/ksh
sort
tcpdump
vi/vim
whois
Please enter the section to update or a new section name: perl 
Please type in the entry to be added to the 'perl' section:
# Interactive Perl debugger
perl -d scriptname

ben@Jotunheim:~$ 

Note that each entry requires a comment as its first line, and is terminated by a blank line. The comment may be continued on the subsequent lines, and is important: the default action for lookup (this may be changed via a setting in the script) is to search only the comment when the script looks for a specific entry in a given section. In other words, if I wanted to find the above entry, I'd search for it as

n perl debug

This would return every entry in the 'perl' section that contained 'debug' in the comment. If you wanted a "looser" type of search, that is, one which would match either the comment or the content, you could change the 'search_opt' variable near the top of the script as described there. Personally, I prefer the "comment-only" search - but, in this, I'm taking advantage of my "Google-fu", that is, my ability to formulate a good search query. The trick is that I also use the same thing, the same mode of thinking, when I create the comment for an entry: I write comments that are likely to match a sensibly-formed query even if I have absolutely no memory of what I originally wrote.

So far, so simple - but that's not all there is to it. The tricky part is the possibly-surprising features that are a natural consequence of the tools that I used to write the script. That is, since I used the regex features of 'sed' to locate the sections and the lines, the arguments provided to the script can also be regexes - at least, if we take care to protect them from shell expansion by using single quotes around them. With those, this script becomes much more useful, since you can now look for more arbitrary strings with more freedom. For example, if I remember that I had an entry mentioning 'tr' in some section, I can tell 'n' to look for 'tr' in the comment, in all the sections:

n '.*' tr

However, if the above search comes back with too many answers (as it will, since 'trailing', 'matrix', 'control', and so on will all match), I can tell 'n' to look for 'tr' as a word - i.e., a string surrounded by non-alphanumeric characters:

 n '.*' '\<tr\>' 

This returns only the match I was looking for.

I can also look for entries in more than one section at once, at least if I remember to use 'sed'-style regular expressions:

n 'perl\|shell' debug

This would search for any entry that has 'debug' in the comments, in both the 'perl' and 'shell' sections.

This can also go the other way:

n perl 'text\|binary'

Or even:

n 'awk\|perl' 'check\|test'

One of the convenient options, when creating a new section, is to use more than one name for it - that is, there may be more than one section name that would make sense to me when I'm thinking of looking it up. E.g., for shell scripting tips and tricks, I might think of the word 'shell' - but I might also think of 'bash', 'ksh', or even 'sh' first. (Note that case isn't an issue: 'n' ignores it when looking up section names.) What to do? Actually, this is quite easy: 'n' allows for multiple names for a given section as long as they are separate words, meaning that they are delimited by any non-alphanumeric character except an underscore. So, if you name a section 'KSH/sh/Bash/shell/script/scripting', any one of these specified as a section name will search in the right place. Better yet, when you add a new entry, specifying any of the above will add it in the right place.

Since the data is kept in a plain text file, you can always edit it if you make a mistake. (The name and the location of the file, '~/.linux_notes', is defined at the top of the script, and each user on a system can have one of their own - or you could all share one.) Just make sure to maintain the format, which is a simple one: section names start with '###' at the beginning of the line, comments start with a single '#', and entries and sections are separated by blank lines. If you do manage to mess it up somehow, it's not a big deal - since the file is processed line by line, every entry stands on its own, and, given the design of the script, processing is fairly robust. (E.g., multiple blank lines are fine; using multiple hashes in front of comments would be OK; even leaving out blank lines between entries would only result in the 'connected' entries being returned together.) Even in the worst case, the output is still usable and readable, and any necessary fixes are easy and obvious.

'n' as a template

One of the things I'd like the readers of LG to take home from this article is the thought of 'n' as a starting place for their own future development of this type of scripts. There are many occasions when you'll want an "update and search interface", and this is a model I've used again and again in those situations, with only minimal adjustments in all cases. Since this script is released under the GPL, please feel free to tweak, improve, and rewrite it however you will. Again, I'd appreciate being notified of any interesting improvements!


Talkback: Discuss this article with The Answer Gang


picture

Ben is the Editor-in-Chief for Linux Gazette and a member of The Answer Gang.

Ben was born in Moscow, Russia in 1962. He became interested in electricity at the tender age of six, promptly demonstrated it by sticking a fork into a socket and starting a fire, and has been falling down technological mineshafts ever since. He has been working with computers since the Elder Days, when they had to be built by soldering parts onto printed circuit boards and programs had to fit into 4k of memory (the recurring nightmares have almost faded, actually.)

His subsequent experiences include creating software in more than two dozen languages, network and database maintenance during the approach of a hurricane, writing articles for publications ranging from sailing magazines to technological journals, and teaching on a variety of topics ranging from Soviet weaponry and IBM hardware repair to Solaris and Linux administration, engineering, and programming. He also has the distinction of setting up the first Linux-based public access network in St. Georges, Bermuda as well as one of the first large-scale Linux-based mail servers in St. Thomas, USVI.

After a seven-year Atlantic/Caribbean cruise under sail and passages up and down the East coast of the US, he is currently anchored in northern Florida. His consulting business presents him with a variety of challenges such as teaching professional advancement courses for Sun Microsystems and providing Open Source solutions for local companies.

His current set of hobbies includes flying, yoga, martial arts, motorcycles, writing, Roman history, and mangling playing with his Ubuntu-based home network, in which he is ably assisted by his wife and son; his Palm Pilot is crammed full of alarms, many of which contain exclamation points.

He has been working with Linux since 1997, and credits it with his complete loss of interest in waging nuclear warfare on parts of the Pacific Northwest.


Copyright © 2010, Ben Okopnik. Released under the Open Publication License unless otherwise noted in the body of the article. Linux Gazette is not produced, sponsored, or endorsed by its prior host, SSC, Inc.

Published in Issue 170 of Linux Gazette, January 2010

Tux