obtém um valor único para cada linha com o comando unix

0

Eu tenho uma lista como abaixo:

1 2 5 2
1 5 5 3
1 5 5 5
5 2 2 2
2 2 4 3

Gostaria de classificar cada linha e obter os valores exclusivos abaixo, algo semelhante a sort | uniq :

1 2 5
1 3 5
1 5
2 5
2 3 4

Eu tenho pesquisado na rede a solução, mas só encontrei a solução para classificar por coluna. Como posso obter o resultado? Desde já, obrigado.

    
por bison72 31.10.2018 / 11:10

5 respostas

4

Como a classificação de linhas é mais fácil do que a classificação de colunas em uma linha, uma abordagem pode ser transpor cada linha (para que cada campo se torne uma linha), aplique sort e uniq e, em seguida, as reverta.

Aqui está uma implementação ingênua:

$ while read -r line; do echo $line | grep -o '[^ ]*' | sort -h | uniq | paste -s; done <file

Ele percorre o file e, para cada linha:

  • Um grep ganancioso com a opção -o (imprime somente a parte correspondente de cada linha) divide sua entrada em linhas n , uma para cada substring correspondente. Aqui estamos combinando tudo, exceto para espaços em branco.
  • As linhas divididas são classificadas com a opção -h , que compara números legíveis por humanos (se você quiser classificar seus campos como sequências alfanuméricas, remova -h ).
  • O comando uniq remove as duplicatas.
  • paste -s imprime cada linha da entrada padrão como campos de uma única linha separados por tabulações. Você pode anexar um | tr '\t' ' ' final para alterar as guias nos espaços.
por 31.10.2018 / 12:19
2

com o Perl:

perl -MList::Util=uniq -alne 'print join " ", sort { $a <=> $b } uniq @F' file
1 2 5
1 3 5
1 5
2 5
2 3 4
    
por 31.10.2018 / 13:21
1

O seguinte não classifica os dados nas colunas, apenas extrai os valores exclusivos. Não está claro se a classificação é necessária.

Usando awk :

$ awk '{ n=split($0,a,FS); $0=""; j=1; delete u; for (i=1; i<=n; i++) if (!u[a[i]]++) $(j++) = a[i]; print }' <file
1 2 5
1 5 3
1 5
5 2
2 4 3

O programa, bem apresentado, com comentários:

{
    # split the current record into fields in the array a
    n = split($0, a, FS)

    # empty the current record
    $0=""

    # j is the next field number that we are to set
    # in the record that we are building
    j=1

    # seen is an associative array that we use to
    # keep track of whether we've seen a bit of
    # data before from this record
    delete seen

    # loop over the entries in a (the original
    # fields of the input data)
    for (i=1; i<=n; i++)
        # if we haven't seen this data before,
        # mark it as seen and...
        if (!seen[a[i]]++)
            # add it to the j:th field in the new record
            $(j++) = a[i]

    print
}

A ideia que eu tenho aqui é construir um registro de saída com os campos únicos dos dados originais, para cada linha de entrada.

"Record" é sinônimo de "line" por padrão, e "field" é sinônimo de "column" (são apenas palavras mais gerais que dependem dos valores atuais em RS e FS ).

    
por 31.10.2018 / 11:24
0

Experimente esta abordagem awk para classificar e uniquificar:

awk '
        {MX = 0                                                 # reset MAX
         split ("", C)                                          # reset C array
         for (i=1; i<=NF; i++)  {C[$i]++                        # for each number encountered, set C element to "true"
                                 if ($i > MX) MX = $i           # record MAX for this line
                                }
         for (i=1; i<=MX; i++) if (C[i]) printf "%s ", i        # only print the index of elements being "true", sorted
         printf ORS                                             # print end-of-line
        }
' file
1 2 5 
1 3 5 
1 5 
2 5 
2 3 4 
    
por 31.10.2018 / 19:09
0

Outra abordagem bash , semelhante à do @ fra-san.

while read X;do tr<<<$X ' ' \n|sort -u|paste -sd" ";done<file
1 2 5
1 3 5
1 5
2 5
2 3 4
    
por 31.10.2018 / 19:25

Tags