imprimir apenas linhas onde a primeira coluna é única

5

Eu estou procurando uma maneira de classificar uma lista e imprimir todas as linhas, cuja primeira coluna aparece apenas uma vez - ou seja, corresponde apenas na primeira coluna. Por exemplo, eu tenho um arquivo onde a primeira coluna é um caminho e a segunda coluna contém um 'tipo'

/path/foo/1 footsy
/path/foo/1 barsy
/path/foo/X barsy
/path/bar/2 footsy
/path/bar/2 barsy
/path/foo/Y footsy

(o arquivo está realmente classificado -k1,1)

Agora, gostaria de extrair apenas casos como

/path/foo/X barsy
/path/foo/Y footsy

Estou pensando em alguma maneira com o awk, onde eu teria que armazenar a linha anterior e comparar o primeiro campo da linha anterior com o campo correspondente na linha atual. Mas eu ainda não tenho uma ideia de como fazê-lo :( Tentei adaptar uma solução encontrada em outra pergunta, mas ela não está funcionando como esperávamos

awk '{
  prev=$0; path=$1; type=$2
  getline
  if ($1 != $path) {
    print prev
  }
}'
    
por THX 12.10.2015 / 17:10

4 respostas

1
  1. awk normalmente lê cada linha da entrada e invoca o script nela. Os casos em que você usaria getline são poucos e distantes entre si. Quando seu script é executado com seis linhas de entrada, esta é uma visão geral do que acontece:

    Read line 1 normally

    Set variables
    Call getline, which reads line 2
    Compare variables

         

    Leia a linha 3 normalmente

         

    Set variables
    Call getline, which reads line 4
    Compare variables

         

    Leia a linha 5 normalmente

         

    Set variables
    Call getline, which reads line 6
    Compare variables

    Obviamente, isso não vai funcionar.

  2. Em segundo lugar, você cometeu um erro comum no seu código awk . Em código%, campos da entrada são referenciados como awk e as variáveis são referenciadas como $number . Isso é diferente dos scripts de shell, onde os argumentos da linha de comando são referenciados como variable_name e as variáveis são referenciadas como $number . Seu teste

    if ($1 != $path)
    

    deve ser

    if ($1 != path)
    
  3. Sua abordagem geral é falha. Você não pode identificar strings que ocorrem apenas uma vez no arquivo olhando duas linhas de cada vez. Eu acredito que você pode fazer isso olhando três linhas de cada vez (isto é, mantendo as duas linhas anteriores nas variáveis), mas coisas assim ficam complicadas e bagunçadas. É provavelmente mais simples contar as ocorrências. Aqui está uma modificação mínima no seu script para fazer isso.

    awk '{
      if ($1 != path) {
        if (count == 1) {
          print prev
        }
        count=1
      }
      else count++
      prev=$0; path=$1
    }
    END {
        if (count == 1) {
          print prev
        }
    }'
    

    Eu deletei $variable_name , já que você nunca usou.

    Divulgação: Isso é essencialmente o mesmo que a última parte da resposta de Glenn.

por 13.10.2015 / 05:09
2

Essas respostas não exigem que a entrada seja classificada:

Armazene a contagem e a última linha vista em matrizes. Requer muita memória para arquivos grandes e requer o GNU awk

gawk '
    {count[$1]++; line[$1]=$0} 
    END {
        PROCINFO["sorted_in"]="@val_str_asc"
        for (key in line) if (count[key] == 1) print line[key]
    }
' file

Digitalize o arquivo duas vezes, primeiro para obter a contagem, depois para imprimir as linhas com contagem 1

awk 'NR == FNR {count[$1]++; next} count[$1]==1' file file

Este será o mais rápido e exigirá menos memória, aproveitando a entrada classificada:

awk '
    prev_key && prev_key != $1 {if (count==1) print prev_line; count=0}
    {prev_key=$1; prev_line=$0; count++}
    END {if (count==1) print prev_line}
' file
    
por 12.10.2015 / 17:57
1

Se o seu shell tiver suporte Substituição de processo e X e Y não contém espaços, guias:

$ grep -Ff <(awk '{print $1" "}' <file | LC_ALL=C uniq -u) <file
/path/foo/X barsy
/path/foo/Y footsy
    
por 12.10.2015 / 18:09
0

Você poderia tentar com isso:

cat text.tx | sort | uniq -c -w11 | fgrep '1 /' | awk '{print $2" "$3}'

com o seu texto.txt como este

]#cat text.txt
/path/foo/1 footsy
/path/foo/1 barsy
/path/foo/X barsy
/path/bar/2 footsy
/path/bar/2 barsy
/path/foo/Y footsy
    
por 13.10.2015 / 01:28