msort(1)foi projetado para poder classificar arquivos com registros de várias linhas. Ele possui uma interface gráfica opcional, além de uma versão normal e utilizável para humanos. (Pelo menos, seres humanos que gostam de ler os manuais com atenção e procurar exemplos ...)
AFAICT, você não pode usar um padrão arbitrário para registros, portanto, a menos que seus registros tenham tamanho fixo (em bytes, não caracteres ou linhas). msortpossui uma -bopção para registros que são blocos de linhas separados por linhas em branco.
Você pode transformar sua entrada em um formato que funcione com -bbastante facilidade, colocando uma linha em branco antes de cada ###...(exceto a primeira).
Por padrão, ele imprime estatísticas no stderr, pelo menos é fácil saber quando não classificou, porque achou que toda a entrada era um único registro.
msort funciona em seus dados. O sedcomando anexa uma nova linha a cada #+linha, exceto a linha 1. -wclassifica o registro inteiro (lexicograficamente). Existem opções para escolher qual parte de um registro usar como chave, mas eu não precisava delas.
Também deixei de fora as novas linhas extras.
$ sed '2,$ s/^#\+/\n&/' unsorted.records | msort -b -w 2>/dev/null
####################################
KEY1
VAL11
VAL12
VAL13
VAL14
####################################
KEY2
VAL21
VAL22
VAL23
VAL24
####################################
KEY3
VAL31
VAL32
VAL33
VAL34
Não tive sorte -r '#'em usar isso como separador de registros. Ele achava que o arquivo inteiro era um registro.