Como posso deixar em branco o nth ao mth campo usando o comando awk?

6

Eu gostaria de resolver o problema abaixo usando o AWK.

Se qualquer outra solução for possível usando idiomas como sed ou Perl, isso também seria muito apreciado.

Abaixo está a entrada:

U,N,UNIX,000,A,5
N,P,SHELL,111,B,6
I,M,UNIX,222,C,7
X,Y,BASH,333,D,8
P,R,SCRIPT,444,E,9

Eu quero a saída como abaixo:

U,N,,,A,5
N,P,,,B,6
I,M,,,C,7
X,Y,,,D,8
P,R,,,E,9

Observe também que: o número total de campos por linha é desconhecido para mim. Eu só sei que os campos 3 e 4 devem ficar em branco.

    
por PriB 19.12.2015 / 07:41

4 respostas

14

Para converter todos os campos do nth para o mth em um comando awk , você não deve codificar os valores; você deve usar um loop "for":

awk 'BEGIN { FS = ","; OFS = ","} {for (i = 3; i <= 4; i++) { $i = "" }; print}' inputfile

Se você deseja excluir um intervalo diferente, ajuste os valores "3" e "4" no código acima.

Explicação:

O bloco BEGIN { ... } é processado antes de olhar para qualquer uma das linhas do arquivo.

OFS define o separador de campo de saída e FS define o separador de campo para entrada. Queremos que ambos sejam vírgulas.

O loop for é como a sintaxe C. Nesse caso, ele executa o seguinte { code block } para i como 3 e como 4.

O $i merece menção porque é totalmente diferente da sintaxe do shell. No shell script, o nome de uma variável deve ser prefixado com $ para expandir para o valor da variável. Não é assim em awk . Em awk , i por si só se expande para seu valor - 3 ou 4 neste caso - e o $ seguido por um número significa o campo nessa posição numerada. Portanto, $i = "" define o campo i th como uma string em branco.

Em seguida, o comando print , fornecido sem argumentos, padroniza a impressão da linha inteira. Na verdade, ele usa todos os campos da linha delimitados por FS e, conforme modificado por qualquer comando anterior, imprime todos eles, separados por OFS e seguidos por uma nova linha no final.

Um comando mais curto equivalente:

Eu sinto que o comando acima é o mais limpo e mais facilmente extensível se você for incluí-lo em um script. É muito explícito sobre o que está fazendo e muito legível. Além disso, tudo pode ser dividido em um script awk autônomo sem alteração; algo que não pode ser feito automaticamente ao usar -v e -F alterna para a invocação awk . (Isso não é razão para não usá-los, é claro. Apenas algo para estar ciente.)

Para um uso único, especialmente, eu usaria o seguinte:

awk -F, -v OFS=, '{for (i = 3; i <= 4; i++) { $i = "" }; print}' inputfile

A opção -F define o valor de FS . A opção -v permite que você defina valores de awk variables na linha de comando.

Em uma nota mais geral, a opção -v pode ser extremamente útil para passar variáveis de shell como variáveis awk: -v myawkvar="$myshellvar" e para alterar o comportamento de tempo de execução de um script awk independente extraído de um arquivo de script com a opção -f scriptname na linha de comando.

    
por 19.12.2015 / 08:09
7
</path/to/in_file awk -v 'FS=,' -v 'OFS=,' '{$3=$4=""; print}'

Explicação

  • </path/to/in_file : leia o arquivo para o padrão.
  • -v 'FS=,' -v 'OFS=,' : configure os separadores de arquivos e o separador de arquivos de saída para , .
  • '{$3=$4=""; print}' : defina os campos 3o e 4o para em branco e, em seguida, imprima a linha inteira (forma abreviada cortesia de jasonwryan ).
por 19.12.2015 / 07:48
6
sed 's/\([^,]*,\)\{2\}/,,/2' <in >out
U,N,,,A,5
N,P,,,B,6
I,M,,,C,7
X,Y,,,D,8
P,R,,,E,9

Isso substitui a segunda ocorrência de um grupo de dois campos consecutivos delimitados por vírgula com duas vírgulas.

Você também pode fazer como:

sed 's/[^,]*//4;s///3' <in >out

... que substitui a 4ª e 3ª ocorrência de uma sequência de qualquer caractere num-não-vírgula sem nada.

Para fazer isso como @Wildcard fez - com um loop escalável:

sed -e:t -e'/\n\{2\}/!s/\(\n*\)[^,]*./\n/3;/\n$/!tt' -e's///;y/\n/,/'

... ou ...

sed -e:t -e's/\n$//;s/\n/&/2;to'  \
    -e's/\(\n*\)[^,]*./\n/3;tt' \
    -e:o -ey/\n/,/

... onde 3 é o número do campo que você deve começar a usar como padrão, , é o delimitador e 2 é o número de campos em branco que você deixaria em branco.

de qualquer forma você escreve ...

sed "$script" <<""
U
N,P
I,M,UNIX
X,Y,BASH,333
P,R,SCRIPT,444,E,9
U
N,P
I,M,
X,Y,,
P,R,,,E,9

... embora você possa precisar usar uma nova linha literal no lugar de n em ... /\n/3 .

    
por 19.12.2015 / 07:52
4

Eu usaria o perl

perl -F, -lane '@F[2,3]=""; print join ",", @F'

Isso usa o -a autosplit, com -F separador de campos da vírgula. -n itera STDIN por linha. Então -e para especificar um script que substitua os campos 2 e 3 (o perl começa do zero) e imprime o resultado.

-l implicitamente remove e adiciona finais de linha.

    
por 19.12.2015 / 12:15

Tags