Comparando campos delimitados

2

Eu tenho dois campos em um arquivo, alunos e professores. Eles são delimitados por um ponto-e-vírgula e quero descobrir quais alunos solteiros têm dois professores diferentes.

jdoe;ateacher
jdoe;bteacher
jsmith;cteacher
bbrown;dteacher
dholden;eteacher

enviaria para:

jdoe;ateacher
jdoe;bteacher

Como posso fazer isso com o shell?

NOTA: Este é o dever de casa. Eu não estou procurando a resposta exata, apenas não sei por onde começar. Eu analisei e canalizei isso de um arquivo para os campos que eu preciso, agora eu só preciso encontrar os dupes, mas não sei como começar.

    
por user1726954 21.09.2014 / 02:49

1 resposta

1

Assumindo que o formato de arquivo que você está mostrando é rígido, de modo que um aluno só apareça duas vezes se tiver dois professores diferentes e que as entradas de um determinado aluno estejam sempre próximas uma da outra, use este comando para encontrar todas as duplicatas. As duplicatas neste arquivo denotam que um aluno tem vários professores, portanto, poderíamos ignorar esse fato.

Exemplo

$ awk -F';' '{ print $1 }' file | uniq -d
jdoe

Isso analisa o arquivo file e o divide usando a opção awk do separador de campos -F';' . Em seguida, instruímos awk a imprimir apenas o primeiro campo, ou seja, os nomes dos alunos. Em seguida, enviamos essa saída para uniq e informamos para imprimir apenas as linhas duplicadas.

Poderíamos então usar essas informações em um loop for e apenas imprimir as linhas que tiverem um aluno na lista retornada pelo comando acima. Aqui está uma estrutura aproximada do aspecto do ciclo:

$ for i in $(..cmd from above..); do 
    ... print lines that contain "$i" ...
done

Aqui estamos pegando a saída do comando awk inicial e estamos passando por ele com um loop for no shell Bash. Essa é tipicamente a abordagem que a maioria das pessoas adotará quando começar.

Exemplo

$ for i in $(awk -F';' '{ print $1 }' file | uniq -d); do \
    grep "^$i;" file; done
jdoe;ateacher
jdoe;bteacher

Esta abordagem, apesar de funcionar, tem alguns problemas. Se os nomes dos arquivos contiverem espaços, essa abordagem falhará. Você pode alternar para um método mais sofisticado como esse usando um loop while.

$ while read; do grep "^$i;" file; done \
    < <(awk -F';' '{ print $1 }' file | uniq -d)
jdoe;ateacher
jdoe;bteacher

Aqui estamos pegando a saída do nosso comando e passando para o loop while assim:

$ while read; do .... ; done < <(...our command...)

Isso tem a vantagem de criar um arquivo temporário com essa notação e passar todos os resultados como linhas para o loop while. Portanto, o comando read agora só precisa analisar os resultados sendo divididos por uma nova linha, em vez de um espaço na implementação do loop for.

< <(...command...)

Exemplo

Veja o que acontece com o loop e espaços:

$ for i in jdoe john smith jjill;do echo "$i"; done
jdoe
john
smith
jjill
    
por 21.09.2014 / 03:21