1

I'm struggling to add <dl> tags around a definition list with sed, and perhaps there is an easier way to do it (I'd love to know).

I would like to search a file for any lines that contain a <dt>, but only match if the previous line does not contain <dt> or <dd>. When a match is found, insert a <dl>.

My attempt so far (which doesn't match at all):

sed '/^((?!<dt>).)*$/ {
    N
    /<dt>/ {
        s/<dt>/<dl><dt>/
    } 
}' file

And the file

# TODO #

 * Set up mail transfer agent
 * Reconfigure timezone

```bash
dpkg-reconfigure tzdata
```

# Hardware #
  <dt>RAM</dt>
  <dd>2GB</dd>

# Partitions #

<dt>`/dev/sda1`</dt>
<dd>/boot</dd>
<dt>`/dev/sda2`</dt>
<dd>/</dd>

The purpose of all of this is to write a parser that converts moinmoin wiki markup to markdown, for porting to a new wiki engine. The defintion list is currently made with the following rule:

sed -i 's/^ \(.*\):: \(.*\)$/  <dt>\1<\/dt>\n  <dd>\2<\/dd>/' file

I would like the output to look like this:

# TODO #

 * Set up mail transfer agent
 * Reconfigure timezone

```bash
dpkg-reconfigure tzdata
```

# Hardware #
<dl>
  <dt>RAM</dt>
  <dd>2GB</dd>
</dl>

# Partitions #

<dl>
<dt>`/dev/sda1`</dt>
<dd>/boot</dd>
<dt>`/dev/sda2`</dt>
<dd>/</dd>
</dl>

Note that I'd like valid html as much as possible. There needs to be a closing tag for each opening tag.

  • ok, but all of you lines match the pattern. is there any way you could provide a (only slightly) more realistic example input and to complement it with a an example of your desired output? it just makes it more simple - less guessing. – mikeserv Dec 8 '15 at 1:55
  • 1
    I've expanded the example file - sorry about that. – stooj Dec 8 '15 at 3:09
  • so you should wrap every pair of sequential <dt>...</dt>\n<dd>...</dt>\n tags within another pair of <dl>...</dl> or an entire sequence of pairs be enclosed? – mikeserv Dec 8 '15 at 3:30
  • An entire sequence of pairs should be enclosed. I've added the desirable output as a demo. – stooj Dec 13 '15 at 2:41
3

This may be more sed than you expect, but I think this is the best way to accomplish what you want with sed.

This script:

  • Inserts a line <dl> before each line that contains a <dt>, if no other <dt>, <dd>, or <dl> preceded the line.

  • Appends a line </dl> after each line containing <dd>, if the line after the line containing <dd> does not contain <dd>.

This sed script uses the sed hold space to remember the previous line so it can be checked for the <d[tdl]> tags before inserting the <dl> tag. It also uses relative addressing ADDR,+N to allow adding the closing </dl> tag. A special case is required to detect if a <dd> is on the last line of the file and needs an appended </dl>. Testing (t and T) and branching (b) are used extensively to implement the logic.

#!/bin/sh

sed '
    /<dt>/ {
        x                  # exchange pattern and hold space
        s/<d[tdl]>//       # subsitutue, just testing for pattern
        g                  # copy hold space back, overwriting pattern space
        t end              # branch to :end if previous subsitution successful
        i \
<dl>
    }

    $ {
        /<dd>/ ! b end     # if <dd> on last line, append </dl>
        a \
</dl>
    }

    /<dd>/,+1 {            # on each line containing <dd> and the line after
        /<dd>/ b end       # if does not contain <dd>, insert </dl>
        i \
</dl>
    }

:end
    h                      # copy pattern space to hold space for next round

' "$@"

This script modifies the sample data to look like this:

[...]

# Hardware #
<dl>
  <dt>RAM</dt>
  <dd>2GB</dd>
</dl>

# Partitions #

<dl>
<dt>`/dev/sda1`</dt>
<dd>/boot</dd>
<dt>`/dev/sda2`</dt>
<dd>/</dd>
</dl>
  • ahh! so wrapped all the way around then! what do you do if you have a validly positioned <dt>...</dt> pair but which is not followed by a validly positioned <dd>...</dd> pair? – mikeserv Dec 8 '15 at 5:57
  • This script as written won't put out the </dl> except after a <dd>. – RobertL Dec 8 '15 at 6:02
  • thats probably correct - i dunno. i would consider it more correct than the other way - but i dunno know how all that tag stuff works. its why i asked (you and the asker) - i wanna do an answer. thank you. oh. but - well - you mean you possibly do the <dl> without the </dl>? i dunno about that.... hm. well. youve got my vote already anyway. shucks. in truth - i didnt even consider the ramifications of that - just that i didnt know if the </dl> should go before the trailing <dt>... – mikeserv Dec 8 '15 at 6:03
  • 2
    The tags are <dl>: definition list, <dt> definition term, <dd> definition data (or definition). I think they want to wrap the <dt>/<dd> sequences with <dl>. On fragments of html in a wiki, we don't really need to be too concerned with the greater xml/html context/validity. So I scripted this to the given input data. If there's a case of <dt> not folowed by <dd>, that would require changes, though it's completely doable. If the answers handle cases not in the sample data, I think it gets too complex and confusing. I hope that helps. – RobertL Dec 8 '15 at 6:28
  • Outstanding. Thanks for the comments as well, I had no idea about the branch thing in sed. – stooj Dec 13 '15 at 2:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.