Coletando os pensamentos da seção de comentários, parece que isso se resume a como grep implementações diferentes decidiram lidar com correspondências vazias, e as expressões [a-z]* correspondem à cadeia vazia.
A opção -o não é definida pelo POSIX, então, como uma implementação lida com ela é deixada para os desenvolvedores.
O GNU grep obviamente joga fora as correspondências vazias, por exemplo, a correspondência da string vazia após once ao usar [a-z]* , e continua a processar a entrada do próximo caractere em diante.
O BSD grep , parece estar acertando o jogo vazio e decide que, por qualquer motivo, é o suficiente e pára por aí.
Stéphane menciona que a versão ast-open de grep entra em um loop infinito na correspondência vazia de [a-z]* após once e não passa desse ponto na sequência.
O OpenBSD grep parece ser diferente do macOS e do FreeBSD grep , pois a adição do sinal -w (que exige que as correspondências sejam delimitadas por limites de palavras) faz com que [a-z]* retorne cada palavra separadamente.
ilkkachu faz a observação de que -o com um padrão que permite corresponder uma cadeia vazia em algum sentido é confuso (ou possivelmente, pelo menos, ambíguo). Todos os jogos vazios devem ser impressos? Na verdade, existem infinitas correspondências depois de cada palavra na string dada.
A fonte do OpenBSD para grep (que exibe o mesmo comportamento de grep no macOS) contém ( src/usr.bin/grep/util.c ):
if (r == 0) {
c = 1;
if (oflag && pmatch.rm_so != pmatch.rm_eo)
goto print;
break;
}
}
if (oflag)
return c;
print:
Isso basicamente diz, se o padrão corresponder ( r == 0 ) e se estivermos usando -o ( oflag ), e se o deslocamento inicial da coincidência for o mesmo que o deslocamento final da correspondência ( pmatch.rm_so == pmatch.rm_eo , ou seja uma partida vazia), o resultado da correspondência é não impresso e a correspondência nessa linha específica de entrada termina ( return c com c == 1 para "correspondência encontrada").