0

I'm looking for a way to remove empty sections in markdown documents, more specifically, changelogs.

For example, if I have:

## Version

### Added
- something

### Removed

### Changed

- something    

### Fixed

I'd like to end up with:

## Version

### Added
- something

### Changed

- something    

Note that Fixed section went away (empty till end of file) and Version section is still there, as I should act on ### sections only.

RomanPerekhrest
  • 88,541
  • 4
  • 65
  • 105
Nikola Knezevic
  • 789
  • 5
  • 20

3 Answers3

1

Awk solution:

awk -v RS="###" 'NF>1{ printf "%s%s",(NR==1? "" : RS),$0 }' file
  • RS="###" - considering ### as record separator

  • NF>1 - if record contains at least 2 fields (including section name)

The output:

## Version

### Added
- something

### Changed

- something    
RomanPerekhrest
  • 88,541
  • 4
  • 65
  • 105
  • Nice, simple and elegant. But not absolutely robust against `###` appearing elsewhere than at the beginning of a level-3 section (e.g. `#### sub-section` or `- something ###`) – Renaud Pacalet Oct 13 '17 at 13:12
  • I agree: nice, simple and elegant, but won't work if a section name may contain more than one word. – duthen Oct 13 '17 at 16:52
1

In plain bash:

$ cat foo.sh
#!/usr/bin/env bash
declare -i p=1 # to-print or not-to-print flag
declare w=""   # buffer
declare l=""   # line
while read l; do
    if [[ $l =~ ^###[^#] ]]; then   # if new section
        (( p )) && printf "%s" "$w" # if to-print, print buffer
        w="$l"$'\n'                 # re-initialize buffer
        p=0                         # reset to-print flag
    else
        w="$w$l"$'\n'             # update buffer
        [[ -n $l ]] && p=1        # if non-empty line, set to-print flag
    fi
done < $1
(( p )) && printf "%s" "$w" # if to-print, print buffer

$ ./foo.sh foo.md
## Version

### Added
- something

### Changed

- something

You can adapt the two tests on read lines if your definition of section and empty section are not exactly what I assumed.

Renaud Pacalet
  • 25,260
  • 3
  • 34
  • 51
1

In plain awk:

#!/usr/bin/awk

BEGIN {
    inside_empty_section = "false"
    buffer = ""
}

/^$/ { # Add the empty line to the current section buffer or print it
    if (inside_empty_section == "true") { buffer = buffer $0 "\n" } else { print $0 }; next }

/^###/ {
    # This is the beginning of a new section.
    # Either the previous one was empty: just forget its buffer.
    # either it was not empty and has already been printed.
    # In any case, just start buffering a new empty section.
    inside_empty_section = "true"
    buffer = $0
    next
}

{
    # Found a non-empty line: the current section is NOT empty.
    # If it was supposed to be empty, print its buffer.
    if (inside_empty_section == "true") { print buffer }
    inside_empty_section = "false"
    print $0
    next
}

Launch it with:

$ awk -f ./test.awk < plop
## Version

### Added
- something

### Changed

- something   

Of course, you could make it more concise:

awk '/^$/ {if (i) {b=b $0 "\n"} else {print $0 }; next} \
/^###/ {i=1; b=$0; next} {if (i) {print b}; i=0; print $0; next}' < plop
duthen
  • 848
  • 6
  • 14
  • All solutions are really good, but I like this one the most. I modified `/^###/ ` to `/^### /`, to make sure only h3 headings are taken into consideration. – Nikola Knezevic Oct 17 '17 at 22:28