sed tips and tricks


I’m creating a Puppet Starter Kit with some standard manifests included and a complete set of documentation. All documentation should be written in Markdown and will be served by Markdoc. But I want to generate all Markdown files from the Puppet manifests, so I only need to document the manifest file. Generating the Markdown is not that difficult, except that I kept ending up with empty lines at the top of the manifest code and I wanted to get rid of those. Of course this should be done with sed, because the whole generation process is written in bash. When playing around with sed I found ~ sed `/./,$!d' filename ~ which, I think, is genius in it’s simplicity. After you find something, do not remove. Life in UNIX and Linux is nice!

The complete (quick and dirty) script is:

#!/bin/bash
# vi: set sw=4 ts=4 ai:

process()
{
    infile="${1}"
    outfile="${2}"
    readme="$(dirname ${infile})/README.md"

    echo "# Puppet manifest documentation"  >> "${outfile}"
    echo "----------"           >> "${outfile}"

    if [[ -f ${readme} ]]
    then
        cp -p ${readme} ${outfile}
    else
        if $(grep -q '^#-- DOC START --#' "${infile}")
        then
            sed -n  -e '/^#-- DOC START --#/,/#-- DOC END --#/p' "${infile}"    | \
                sed -e '/^#-- DOC START --#/d'                                  \
                    -e '/^#-- DOC END --#/d'                                    \
                    -e 's/^#//'                                                 \
                    -e 's/^ //' > "${outfile}"
        else
            echo "### No infile documentation available" >> "${outfile}"
        fi
        echo "----------"               >> "${outfile}"
        echo "# Puppet manifest"        >> "${outfile}"
        echo "### File: \`${file}\`"    >> "${outfile}"
        echo "~~~~~~~~~~ {.puppet}"     >> "${outfile}"

        if $(grep -q '^#-- DOC START --#' "${infile}")
        then
            sed -e '1,/^#-- DOC END --#/d' "${infile}"      \
                -e '/^# `\$[I]d/d'                          \
                -e '/./,$!d' >> "${outfile}"
        else
            cat "${f}" >> "${outfile}"
        fi
    fi
    echo "~~~~~~~~~~" >> "${outfile}"

    echo "<br />" >> "${outfile}"
    if $(grep -q '`\$Id' "${infile}")
    then
        grep '`\$[I]d' "${infile}" | sed -e 's/^# *//' -e 's/\(`\$[I]d\)/Version: \1/' >> "${outfile}"
        echo "----------" >> "${outfile}"
    fi
}

TOPDIR="$(puppet config print confdir)/doc"
if [[ ! -d ${TOPDIR} ]]
then
    echo "The top documentation directory cannot be found (${TOPDIR})"
    exit 1
fi

cd ${TOPDIR} ||
{   echo "Cannot change to the documentation directory"
    exit 1
}

if [[ -d todo ]]
then
    echo "# Todo list for Puppet" > todo/index.md
    for f in $(ls todo/*.md | grep -v 'index.md$')
    do
        file=$(basename ${f})
        md=$(basename ${f} .md)
        echo "* [${md}](${md})" >> todo/index.md
    done
fi

find puppet -type f | grep -v '/\.svn/' | xargs rm -f
envs="production develop"

echo "# Puppet manifests" > puppet/index.md
mkdir -p puppet

# Quick and dirty file selection
for f in $(ls   ../manifests/*.pp           \
                ../manifests/*/*.pp         \
                ../manifests/*/*/*.pp       \
                ../manifests/*/*/*/*.pp     \
                ../manifests/*/*/*/*/*.pp   \
                ../manifests/*/*/*/*/*/*.pp \
                    2>/dev/null)
do
    file=$(basename ${f})
    md=$(basename ${f} .pp)

    if [[ "${md}" = "init" ]]
    then
        md=$(basename $(dirname $(dirname ${f})))
    fi
    echo "* [${md}]($md)" >> puppet/${env}/index.md

    process "${f}" "puppet/${md}.md"
done

echo "# Puppet modules" >> puppet/index.md
for env in ${envs}
do
    mkdir -p puppet/${env}
    echo "* [${env}](${env})" >> puppet/index.md
    echo "# Puppet environment \`${env}\`" > puppet/${env}/index.md

    for f in $(ls ../modules/${env}/*/manifests/*.pp 2>/dev/null)
    do
        file=$(basename ${f})
        md=$(basename ${f} .pp)

        if [[ "${md}" = "init" ]]
        then
            md=$(basename $(dirname $(dirname ${f})))
        fi
        echo "* [${md}]($md)" >> puppet/${env}/index.md

        process "${f}" "puppet/${env}/${md}.md"
    done
done


if [[ x"${1:-}" = x"-a" ]]
then
    rm -rf resources
    mkdir resources
    cd resources

    echo "# All known resources for Puppet version $(puppet -V)" > index.md
    echo "| Resource name | Description |" >> index.md
    echo "|---------------|-------------|" >> index.md
    puppet describe --list | awk 'NR > 1 { print }' | sort | while read res min desc
    do
        echo "| [\`${res}\`](${res}) | ${desc} |" >> index.md
        puppet describe "${res}" > "${res}.md"
    done
    cd ..
fi

markdoc build
sysadm  linux  code 

See also