Como apagar o campo de entrada no AWK?

7

Estou transformando alguns dados com awk (ou gawk ) e quero excluir um dos campos de entrada antes de imprimir a saída novamente.

O que eu quero alcançar é isto:

~ $ echo 'field1,field2,field3' | awk -F, '{transform($1); delete($2); print $0;}'
new_field1,field3

Não posso simplesmente atribuir uma string vazia a $2 , porque isso leva a new_field1,,field3 (observe as duas vírgulas).

Eu poderia imprimir explicitamente apenas os campos que eu quero, mas isso não é muito elegante, porque eu tenho muito mais campos do que 3 e também há campos opcionais no final (não mostrados aqui). É por isso que prefiro print $0 . Só precisa se livrar de alguns campos primeiro.

Alguma ideia?

    
por MLu 19.02.2018 / 01:14

2 respostas

7

A exclusão de campos no awk é notoriamente difícil. Parece ser uma operação tão simples (e geralmente necessária), mas é mais difícil do que deveria.

Veja Existe uma maneira de excluir completamente os campos no awk, para que os delimitadores extras não sejam impressos? do Stack Overflow para uma boa maneira de fazer isso.

Eu copiei a função rmcol() na resposta do @ ghoti, para que tenhamos uma cópia aqui no U & L:

function rmcol(col,     i) {
  for (i=col; i<NF; i++) {
    $i=$(i+1)
  }
  NF--
}

Exclui a coluna especificada da linha de entrada atual e diminui o contador de campo ( NF ) para corresponder.

Não faço ideia do que a sua função transform() faz, por isso nem vou tentar duplicar isso - mas aqui está um exemplo de usar rmcol() em awk one-liner:

$ echo 'field1,field2,field3' | awk -F, -v OFS=, '
  function rmcol(col,     i) {
    for (i=col; i<NF; i++) {
      $i=$(i+1)
    }
    NF--
  }

  { rmcol(2); print; }
  '
field1,field3

BTW, se você precisar excluir vários campos de uma linha de entrada, é melhor / mais fácil excluí-los na ordem inversa. Ou seja, exclua os campos com numeração mais alta primeiro . Por quê? Porque os campos com numeração mais alta serão renumerados toda vez que você excluir um campo com numeração mais baixa, dificultando muito o controle de qual número de campo pertence a qual campo.

BTW, delete() in awk é para excluir elementos de uma matriz - não para excluir campos de uma linha de entrada. Você poderia split() de cada linha de entrada (em FS ) em uma matriz e excluir o 2º elemento da matriz, mas teria que gravar uma função join() para imprimir a matriz com uma vírgula (ou OFS ) separando cada campo.

Mesmo isso seria mais complicado do que seria de se esperar, porque todos os arrays em awk são matrizes associativas (ou seja, não numericamente indexados) - então delete(array[2]) won ' t muda automaticamente os elementos da matriz 3+ para os elementos 2+. Você teria que escrever sua própria função de wrapper em torno de delete() para fazer praticamente a mesma coisa para arrays que rmcol() faz para campos de entrada.

    
por 19.02.2018 / 02:43
4

Algumas alternativas

1) pré-processe a entrada para remover o campo primeiro, fácil de fazer com cut se o separador de campo for um único caractere

$ s='field1,field2,field3'
$ # use 'cut -d, -f1,3-' if --complement option is not available
$ echo "$s" | cut -d, --complement -f2
field1,field3
$ echo "$s" | cut -d, --complement -f2 | awk 'BEGIN{FS=OFS=","} {$1="new"} 1'
new,field3

2) use perl

$ # indexing starts from 0, the array @F contains the input fields
$ # $#F will give index of last element in the array
$ echo "$s" | perl -F, -lane '$F[0]="new"; print join ",", @F[0,2..$#F]'
new,field3
    
por 19.02.2018 / 06:25

Tags