Você obtém esse resultado porque o loop primeiro executa grep ab file e retorna todas as ocorrências de ab na primeira iteração e após esse loop executa grep cd file e retorna todas as ocorrências cd em file .
Você não precisa de um loop for . Tente isto :
grep -e "^ab$" -e "^cd$" file
Ou use a opção -x para selecionar apenas as correspondências que correspondem exatamente à linha inteira (de man grep , thx a Kusalananda :
grep -x -e "ab" -e "cd" file
A saída será:
ab
cd
ab
Ou (assumindo que o GNU grep ou compatível como \| não seja um operador BRE padrão):
grep "^\(ab\|cd\)$" file
O mesmo com o GNU sed :
sed '/^\(ab\|cd\)$/!d' file