+
AMDG
Goodman Coat of Arms

Goodman's Oak

Miscellaneous Thoughts, Projects, and Ruminations on Life, the Universe, and Everything

The Power of sed

Donald P. Goodman III 26 Dec 1200 (30 Dec 2016)

So sed has gone a bit out of fashion lately, what with all the johnny-come-latelies like Perl, Python, and Ruby running around disrupting prior paradigms of computer usage. But I personally still think there's room for dedicated tools like sed and awk, rather than general-purpose programming languages, for given tasks. The following is my experience where sed, in particular, was precisely the tool I needed for the job.

I was working on dozcal, in the dozenal suite; specifically, I was working on a Lua script that would enter the traditional Catholic liturgical calendar into the program. As I moved on from the propers to the common, I realized that I was going to need to keep track of which dates had already been entered, so that the script could appropriately downgrade the feast (say, make a III Class into a commemoration) if necessary. That meant that I had to add a line near some 7 dozen points in the code; that new line needed to contain a piece of the same text as occurred two lines below it. Let me explain.

A typical code block looked like this:

returntab[index] = {
	START_DATE=print_date(jdn_to_greg(jdn_easter)),
		TITLE="Easter",CLASS="Catholic:  1176",
		START_TIME="",END_TIME="",
		CATEGORY="I Class,traditional,catholic,easter",
		LOCATION=""
}
index = index + 1

I needed it to look like this:

table.insert(usedvals,jdn_easter)
returntab[index] = {
	START_DATE=print_date(jdn_to_greg(jdn_easter)),
		TITLE="Easter",CLASS="Catholic:  1176",
		START_TIME="",END_TIME="",
		CATEGORY="I Class,traditional,catholic,easter",
		LOCATION=""
}
index = index + 1

Note that the new line on the top includes the text jdn_easter; this must always be the same text as in the innermost parentheses in the line two below it, beginning with START_DATE. What to do?

I could have banged something together in Perl; but that's a bit overkill, isn't it? This is definitely a job for sed, the stream editor which enables you to edit a text file without the overhead of an entire programming language. It uses pretty standard regular expressions to make matches and replacements; essentially, it reads each line into its pattern space, performs whatever edits you've instructed it to perform, then outputs that line and moves on. Very simple.

You can, though, tell it to read an extra line (or lines) into the pattern space, with N. So that's what I did. All in all, it took me about half an hour to write and execute this script; it woudl have taken me much longer to make the edits manually, or to write a script in a full programming language designed to do anything, rather than this one task.

(Of course, it would have been better still if I'd encapsulated this operation in a function; but that doesn't change the position I was in, which required lots of complex error-prone edits throughout a file.)

Here's the entirety of the script:

# +AMDG

/returntab\[index\] = {/{
	N
	s/\n//
	s/.*(\([^()]*\)).*/\ttable.insert(usedvals,\1)\n&/
	s/returntab\[index\] = {/&\n/
}

Very simply, this finds a line which contains the string returntab[index] = {, then performs a group of operations on it, which are contained in the braces.

Running my program through this little sed script did everything I needed. This is the power of sed: a tool designed to bang text around and do it well, nothing more and nothing less.