identificando palavras no arquivo 1 e combinando-as com o segundo arquivo e imprimindo-as

2

Eu tenho dois arquivos da seguinte forma      Arquivo 1 tem lista de IDs

ram
Tarah
taikah
jhdfklj

Arquivo 2 tem

id=ram;*between*hkjhdswklf     kjsdklhglshglkhgklsdgkhdfg
id=taikah;jhdjfkhg**kjdfkjksfdjk dfkjskdfjkljdflkjsdflj

Basicamente, quero corresponder os IDs do arquivo 1 ao arquivo 2 e colar a linha completa do arquivo 2

A ajuda seria apreciada.

    
por Bio_Ram 15.03.2014 / 10:02

5 respostas

3

O Awk foi projetado para isso (aqui com GNU awk para a captura de padrões de chaves combinadas em uma matriz em match() ):

$ awk '
    NR == FNR { Ids[$1]++; next; }
    match($1, /id=([^;]+);/, Id) && Id[1] in Ids
' /file1 /file2
id=ram;*between*hkjhdswklf     kjsdklhglshglkhgklsdgkhdfg
id=taikah;jhdjfkhg**kjdfkjksfdjk dfkjskdfjkljdflkjsdflj

Várias das respostas alternativas propõem soluções que fazem várias suposições sobre o insumo que não são exatamente evidentes na amostra dada pelo OP. Uma coisa que é clara, porém, é que existe uma palavra legítima nos dados após id=xxx; , por isso não é seguro assumir que um dos IDs de File 1 pode não estar em algum lugar em os dados também.

É por isso que o Awk é muito melhor que o fgrep(1) . O awk dividirá a entrada em campos que podem limitar a correspondência possível, o que foi feito no exemplo acima, especificando que a correspondência deve estar no primeiro campo ( $1 ).

A entrada de exemplo contém alguns dados ao lado do ID, portanto, se você quiser uma maneira completa de comparar os IDs em File 1 , defina o separador de campos ( FS ) como ; e imprima somente se houver uma correspondência exata.

Suponha que File 2 contenha:

id=ram;*between*hkjhdswklf     kjsdklhglshglkhgklsdgkhdfg
id=taikah;jhdjfkhg**kjdfkjksfdjk dfkjskdfjkljdflkjsdflj
id=notinlist;jhdjfkhg**kjdfkjksfdjk ram*dfkjskdfjkljdflkjsdflj
id=notinlist;*between*taikah*hkjhdswklf     kjsdklhglshglkhgklsdgkhdfg

Então, o seguinte ainda funcionará

$ awk -v FS=";" '
    NR == FNR {Id = "id=" $0; Ids[Id]++; next; }
    $1 in Ids
' /file1 /file2
id=ram;*between*hkjhdswklf     kjsdklhglshglkhgklsdgkhdfg
id=taikah;jhdjfkhg**kjdfkjksfdjk dfkjskdfjkljdflkjsdflj

Note que a outra solução Awk será muito lenta para arquivos grandes, já que é O (N 2 ). A sed versão funcionará, mas, pessoalmente, acho que sed é ilegível para os meros mortais.

    
por 15.03.2014 / 10:45
3

Isso é exatamente o que fgrep(1) foi criado para ... ou mais modernamente (pode usar as extensões GNU):

grep -Fwf file1 file2

De man grep :

   -F, --fixed-strings
          Interpret PATTERN as a  list  of  fixed  strings,  separated  by
          newlines,  any  of  which is to be matched.  (-F is specified by
          POSIX.)
   -f FILE, --file=FILE
          Obtain  patterns  from  FILE,  one  per  line.   The  empty file
          contains zero patterns, and therefore matches nothing.   (-f  is
          specified by POSIX.)
   -w, --word-regexp
          Select  only  those  lines  containing  matches  that form whole
          words.  The test is that the matching substring must  either  be
          at  the  beginning  of  the  line,  or  preceded  by  a non-word
          constituent character.  Similarly, it must be either at the  end
          of  the  line  or  followed by a non-word constituent character.
          Word-constituent  characters  are  letters,  digits,   and   the
          underscore.
    
por 15.03.2014 / 18:40
2

Aqui está uma maneira de fazer isso:

awk 'FNR==NR{ids[$0]=$0;next}{for(id in ids){if($0 ~ id){print}}}' file1 file2
    
por 15.03.2014 / 13:37
2

Outro sed um complicado:

echo | sed -e '1,/^$/{H;d;}
               G;/id=\([^;]\{1,\}\);.*\n\n/!d
               s/\n.*//' file1 - file2

Uma versão POSIX myq , com a mesma limitação:

awk '
  NR == FNR { Ids[$1]++; next; }
  match($0, /id=[^;]+/) && substr($0, RSTART+3, RLENGTH-3) in Ids
' file1 file2

Uma abordagem usando grep :

sed 's/.*/id=&;/' file1 | grep -Ff - file2
    
por 15.03.2014 / 11:18
1
    sed -rfn - <<-HD ./file2
        $(sed -r 's|.*|s/id=&/\&/p|' ./file1)
    HD

Isso funciona. Primeiro, ele lê somente file1 e transforma cada uma de suas linhas em uma expressão sed regular para file2 , w ritando cada uma dessas linhas para sua stdout , que é a subshell executada aqui. document atribuído ao segundo descritor de arquivo sed <<-HD 0. Portanto, sed invocação, o segundo extrai sed da primeira saída como seu padrão de expressão regular de - stdin . Seu script é assim:

    s/id=ram/&/p
    s/id=Tarah/&/p
    s/id=taikah/&/p

O acima é o script que a segunda invocação de sed é executada. Como a linha every em file1 é transformada em uma correspondência literal id = * para file2 sem depender de nenhum endereço de referência antes do i in id ou após o seguinte ;semicolon ; Eu não acho que poderia saudades nada, e deve (espero que agora) ser muito fácil de alterar para lidar com quaisquer variações na necessidade original, conforme necessário.

E quando sed dois é concluído, a saída é assim:

    id=ram;*between*hkjhdswklf     kjsdklhglshglkhgklsdgkhdfg
    id=taikah;jhdjfkhg**kjdfkjksfdjk dfkjskdfjkljdflkjsdflj

P.S. Se você quiser uma boa risada, dê uma olhada no histórico de edição desta resposta - meu caminho para essa solução foi mais do que apenas um pouco confuso - foi um desastre de trem.

    
por 15.03.2014 / 13:58