The Power of sed
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.
- First, it reads in another line (N).
- Then, it replaces the newlines in that line with nothing, effectively stripping the newline and making the two into one line (s/]n//).
- Then, it finds the string in the innermost set of parentheses in the new line and saves it, (s/.*(\([^()]*\)).*/); the parens escaped with backslashes save the enclosed string, and are not part of the pattern being matched. Notice that .* is included both before and after this; this means that we're matching the entire pattern space, but we're only saving the contents of the innermost parens. We then replace the pattern space with the line we need to add, including the contents we saved with the backslashed parentheses (\ttable.insert(usedvals,\1)\n), followed by a newline so it'll look nice in the output, then we include the entire pattern we matched before, including the innermost parens, without any change (&/).
- Finally, we put the newline back in where we'd taken it out before, to keep our final output nicely formatted (s/returntab\[index\] = {/&\n/), then close out the editing commands (}).
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.