Today is Valentine’s day. Valentine’s day is all about love, and the internationally recognised symbol of love is the stylised heart. I love sed. This is probably why I spent this evening alone writing a filter to format text as ASCII hearts:

nick@pickle:~$ sed --posix -n -f hearts.sed < poem
      Shall        I comp
    are thee t   o a summer
  's day? Thou  art more love
 ly and more temperate. Rough 
 winds do shake the darling bu
 ds of May, And summer's lease
   hath all too short a date.
     Sometime too hot the e
      ye of heaven shines
        , And often is 
          his gold co
            mplexio
              n d
               i
      mmed;        And ev
    ery fair f   rom fair s
  ometime decli nes, By chanc
 e, or nature's changing cours
 e untrimmed. But thy eternal 
 summer shall not fade Nor los
  e possession of that fair t
    hou ow'st; Nor shall de
      ath brag thou wand'
        rest in his sha
          de, When in
             eterna
              l l
               i
      nes to        time 
    thou grow'   st, So lon
  g as men can  breathe or ey
 es can see, So long lives thi
 s, and this gives life to the
 e............................
  ...........................
    .......................
      ...................
        ...............
          ...........
            .......
              ...
               .
 

You can download the script here: hearts.sed.

As far as I can tell, girls are mad keen on standards compliance. So I went out of my way to make this script POSIX compliant.

I've reproduced this labour of love below. As you can see, it demonstrates the elegance and practicality of sed as general purpose programming language:

: gather
/^[[:space:]]*$/d
s/[[:space:]]\{1,\}/ /g
/.\{251\}/{
  s/^\(.\{6\}\)\(.\{6\}\)/     \1       \2\n1/;P
  s/^.*\n1\(.\{10\}\)\(.\{10\}\)/   \1   \2\n2/;P
  s/^.*\n2\(.\{13\}\)\(.\{13\}\)/ \1 \2\n3/;P
  s/^.*\n3\(.\{29\}\)/\1\n4/;P
  s/^.*\n4\(.\{29\}\)/\1\n5/;P
  s/^.*\n5\(.\{29\}\)/\1\n6/;P
  s/^.*\n6\(.\{27\}\)/ \1\n7/;P
  s/^.*\n7\(.\{23\}\)/   \1\n8/;P
  s/^.*\n8\(.\{19\}\)/     \1\n9/;P
  s/^.*\n9\(.\{15\}\)/       \1\nA/;P
  s/^.*\nA\(.\{11\}\)/         \1\nB/;P
  s/^.*\nB\(.\{7\}\)/           \1\nC/;P
  s/^.*\nC\(.\{3\}\)/             \1\nD/;P
  s/^.*\nD\(.\)/              \1\n/;P
  $ { d; q }
  D
}
$ { s/$/\./; b gather }
N
s/\n\n*/ /g
b gather