Supondo que os arquivos estejam todos ordenados e que estamos usando um shell que entenda as substituições do processo (como bash ):
$ join -t . -v 1 -o 0 <( join -t . a.txt b.txt ) c.txt
c
ou, para outras camadas,
$ join -t . a.txt b.txt | join -t . -v 1 -o 0 - c.txt
c
Isso usa join duas vezes para realizar junções relacionais entre os arquivos. Os dados são interpretados como campos delimitados por pontos (com -t . ).
A junção entre a.txt e b.txt é direta e produz
a.up
b.up
c.up
Estas são todas as linhas dos dois arquivos cujo primeiro campo delimitado por ponto ocorre nos dois arquivos. A saída consiste no campo de junção ( a , b , c ) seguido pelos outros campos de ambos os arquivos (somente b.txt possui dados adicionais).
A segunda junção é um pouco mais especial. Com -v 1 , pedimos para ver as entradas no primeiro arquivo (o resultado intermediário acima) que não podem ser emparelhadas com nenhuma linha no segundo arquivo, c.txt . Além disso, pedimos apenas para ver o próprio campo de associação ( -o 0 ). Sem o -o flag, obteríamos c.up como resultado.
Se os arquivos não forem classificados, cada ocorrência de um nome de arquivo file poderá ser substituída por <( sort file ) no comando.