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.

4
  • 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
    Commented Dec 8, 2015 at 1:55
  • 1
    I've expanded the example file - sorry about that.
    – stooj
    Commented Dec 8, 2015 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
    Commented Dec 8, 2015 at 3:30
  • An entire sequence of pairs should be enclosed. I've added the desirable output as a demo.
    – stooj
    Commented Dec 13, 2015 at 2:41

1 Answer 1

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>
5
  • 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
    Commented Dec 8, 2015 at 5:57
  • This script as written won't put out the </dl> except after a <dd>.
    – RobertL
    Commented Dec 8, 2015 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
    Commented Dec 8, 2015 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
    Commented Dec 8, 2015 at 6:28
  • Outstanding. Thanks for the comments as well, I had no idea about the branch thing in sed.
    – stooj
    Commented Dec 13, 2015 at 2:40

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .