Como imprimir todos os campos contendo uma das duas strings em uma tabela com awk

1

Eu tenho uma tabela com muitas linhas e um número variável de colunas por linha.

Em cada linha, eu só quero imprimir o primeiro campo e todos os campos contendo uma das duas seqüências (neste exemplo, eu quero todos os campos contendo as palavras cachorro e vaca).

Por exemplo:

A   dog999   dog284   cow284   pig383   pig234   cow432   chicken432
B   cow394   cow432   cow345   dog983   pig345   chicken532 
C   dog847   pig357   pig236   cow395   dog496
D   dog392   cow237   cow749

Saída desejada:

A   dog999   dog284   cow284   cow432   
B   cow394   cow432   cow345   dog983   
C   dog847   cow395   dog496
D   dog392   cow237   cow749

Até agora, com awk eu tenho:

awk -v OFS='\t' '{for (i = 1; i <= NF; i++) {if ($i ~ /dog/) print $1,$i; else if ($i ~ /cow/) print $1,$i} }' file.txt

Mas isso resulta em uma linha para cada campo que contém uma dessas duas strings.

    
por shenuhcide 20.09.2016 / 02:36

3 respostas

2

Você estava perto, mas precisava extrair o primeiro valor, porque não quer imprimir isso para cada palavra correspondente. Podemos usar printf para evitar novas linhas.

awk '{printf "%s",$1
      for (i=1;i<=NF;i++)
      {
        if ($i ~ /dog|cow/) { printf " %s",$i; }
      }
      print ""
     }'

A saída seria:

A dog999 dog284 cow284 cow432
B cow394 cow432 cow345 dog983
C dog847 cow395 dog496
D dog392 cow237 cow749

Isso pode ser reduzido a uma linha:

awk '{printf "%s",$1; for (i=1;i<=NF;i++) { if ($i ~ /dog|cow/) { printf " %s",$i; }  } print ""  }'

Observe que isso imprimirá uma linha que não corresponde a nenhuma palavra eg

E pig sheep

irá produzir

E
    
por 20.09.2016 / 03:49
2

Se perl solução estiver bem:

$ cat ip.txt 
A   dog999   dog284   cow284   pig383   pig234   cow432   chicken432
B   cow394   cow432   cow345   dog983   pig345   chicken532 
C   dog847   pig357   pig236   cow395   dog496
D   dog392   cow237   cow749

$ perl -lane 'print join("\t",$F[0],grep {/cow|dog/} @F[1..$#F])' ip.txt 
A   dog999  dog284  cow284  cow432
B   cow394  cow432  cow345  dog983
C   dog847  cow395  dog496
D   dog392  cow237  cow749
  • -a divide a linha de entrada nos espaços e salva em @F array
  • -l tira linhas novas da entrada e adiciona de volta ao imprimir
  • join adicionará \t entre os elementos ao imprimir
  • $F[0],grep {/cow|dog/} @F[1..$#F] primeiro elemento da matriz e todos os elementos correspondentes a cow ou dog
  • Também pode usar perl -lape'$_=join"\t",shift(@F),grep/cow|dog/,@F' . aqui shift irá apagar e retornar o primeiro elemento da matriz @F , atribuindo o resultado a $_ será impresso no final da cortesia -p option (Dica do chapéu para Stéphane Chazelas )


Se as linhas que não contêm cow ou dog devem ser ignoradas:

perl -lane 'print join("\t",$F[0],grep {//} @F[1..$#F]) if /cow|dog/' ip.txt 
    
por 20.09.2016 / 04:26
0

TXR macro do awk :

$ txr -e '(awk (:let tmp)
               (:begin (set ofs "\t"))                     
               (f (set tmp (pop f))
                  (ff (keep-if #/cow|dog/))
                  (push tmp f) (prn)))' data
A   dog999  dog284  cow284  cow432
B   cow394  cow432  cow345  dog983
C   dog847  cow395  dog496
D   dog392  cow237  cow749

Divisão:

  1. A cláusula :let na macro especifica as variáveis locais. Esta macro implementa o "Awk Paradigm", mas em uma linguagem segura de tipos, na qual as variáveis devem ser definidas antes do uso. Portanto, além de cláusulas como :begin e :end (análogo a BEGIN e END no POSIX Awk), esse Awk fornece :let para definir variáveis lexicamente com escopo definido para a macro.

  2. (f (set tmp (pop f)) ...) é uma cláusula de condição-ação, em que a condição é f . Se é a lista de campos delimitados do registro; se não estiver vazio (não é igual a nil ), então ele se comportará como verdadeiro booleano. Portanto, os formulários de ação são executados se houver algo em f .

  3. (set tmp (pop f)) exibe o primeiro campo da lista e o salva na variável temporária tmp . O segundo campo se torna primeiro, o terceiro segundo e assim por diante. Quando operamos em f , o registro rec também é reconstituído automaticamente usando ofs , assim como no POSIX Awk, o registro $0 é reconstituído usando OFS entre os campos.

  4. (ff ...) filtra os campos por meio de uma operação, neste caso (keep-if #/regex/) . Basicamente, removemos de f todos os campos que não correspondem ao regex. ff é um operador visível dentro da macro awk . keep-if é uma função regular; aqui está implicitamente curry, então o argumento da lista não aparece. Ele espera uma função de predicado, mas um regex é chamado por função, tão adequado quanto um predicado.

  5. Em seguida, enviamos o primeiro campo salvo anteriormente para a lista de campos f com (push tmp f) .

  6. (prn) é o equivalente a print . Sem argumentos, imprime o registro, seguido pelo separador de registro de saída ( ors ) que é inicializado para nova linha. Como rec foi reconstituído depois de todas as manipulações de f , obtemos a saída filtrada.

Como pode ser visto, o paradigma Awk está basicamente intacto, apenas no contexto de uma linguagem diferente na qual diferentes tipos de coisas são possíveis. A conveniência de poder fazer $2 > $1 sem verificar se esses campos realmente existem não está lá; mas, por outro lado, não precisamos escrever loops para processar os campos como uma estrutura de dados. Os campos podem ser mapeados por meio de funções ou tratados como uma pilha.

A solução Perl da Sundeep traduz aproximadamente a macro awk da seguinte forma:

$ txr -e '(awk (t (prn '@[f 0]\t@{(keep-if #/cow|dog/ [f 1..:]) "\t"}')))' data
    
por 22.09.2016 / 19:29

Tags