Como obter linhas exclusivas com base no valor de uma coluna

3

Após a entrada:

A 13
A 12
B 17
C 33
D 344
C 24
A 5
C 99

Eu quero obter apenas as linhas em que a coluna um é única:

B 17
D 344

Uma solução com awk seria boa, mas outra coisa também é aceitável.

    
por Michael 04.04.2017 / 11:47

5 respostas

4

com awk :

awk 'NR==FNR { a[$1]++ } NR!=FNR && a[$1]==1' file file

(o nome do arquivo é passado duas vezes).

Editar: Se o arquivo vier de stdin , você precisará de uma cópia temporária. Algo parecido com isto:

tmp="$( mktemp -t "${0##*/}"_"$$"_.XXXXXXXX )" && \
    trap 'rm -f "$tmp"' 0 HUP INT QUIT TERM || exit 1
... | tee "$tmp" | awk '...' - "$tmp"
    
por 04.04.2017 / 12:44
6

Se você não se importa de embaralhar o pedido,

sort <file> | uniq -uw 1

Veja man uniq para mais informações, mas aqui estão as partes importantes.

   -u, --unique
          only print unique lines
   -w, --check-chars=N
          compare no more than N characters in lines
    
por 04.04.2017 / 12:19
5

Se você quiser awk

awk '
    $1 in ARR{
        ARR[$1] = RS;
        next;
    }
    {
        ARR[$1] = $0;
    }
    END{
        for(i in ARR)
            if(ARR[i] != RS)
                print ARR[i];
    }
    ' file

O script coloca as linhas na matriz ARR com o primeiro campo como um índice e a linha completa como um valor. Se o array já tiver o mesmo valor de alteração de índice para o sinal «\ n» (nova linha). Após o término do arquivo, imprime os elementos da matriz cujo valor não é igual a "\ n"
Ser informado que a variável RS do awk é igual a newline por padrão.

Ou você pode fazer isso por sed

sort file |
sed '
    :a;
    $!N;
    s/\(\S\+\s\).*\n.*/\a/;
    ta;
    /\a/P;
    D;
    '
    
por 04.04.2017 / 12:34
3
$ cut -d' ' -f1 <file | sort | uniq -d | sed 's/^/^/' | grep -v -f /dev/stdin file
B 17
D 344

Isso primeiro seleciona as entradas duplicadas na primeira coluna do arquivo file cortando a coluna, classificando-a e alimentando-a em uniq -d (que relatará somente duplicatas).

Em seguida, ele prefixará cada linha resultante com ^ para criar expressões regulares que estejam ancoradas no início da linha. A saída do comando sed com os dados fornecidos é

^A
^C

O% final grep lê essas expressões regulares e seleciona todas as linhas do arquivo que não corresponde a nenhuma delas. Recebemos grep para ler os padrões de sed usando -f /dev/stdin .

O resultado terá a mesma ordem do arquivo original.

    
por 04.04.2017 / 17:00
2
perl -lane '
   exists $h{$F[0]} and undef $h{$F[0]},next;

   ( $h{$F[0]}, $h[@h] ) = ( $_, $F[0] );

   END{ print $h{$_} for grep { defined $h{$_} } @h }
' yourfile

A operação do código analisa se o primeiro campo foi encontrado antes, então a chave por esse nome existiria no hash, e assim vamos em frente e undef o valor para esta chave em particular, como não faz sentido construir um array que seja descartado no final. Em vez disso, carregamos a mesma informação por uma impressão de menor memória.

E no cenário de ver o primeiro campo na primeira vez, nós preenchemos o hash %h com a linha atual e, simultaneamente, anexamos a matriz @h com essa chave. Realizamos este passo para manter a ordem em que as chaves foram encontradas. Se não nos importamos com o pedido, podemos muito bem acabar com este passo.

Finalmente, quando todas as entradas foram digeridas, no final END block, faça um loop sobre os elementos da matriz @h e daqueles que pescam apenas aqueles para os quais o hash %h tem valores definidos. Lembre-se, undef significa que os valores devem ter visto mais de uma vez.

    
por 04.04.2017 / 15:03