Trate uma coluna com texto que tenha espaços como 1 campo

0

Eu tenho um arquivo com o seguinte formato:
TEXTO INTEIRO INTEGER

O texto é unicode e pode ter espaços.
Estou tentando usar o awk para imprimir o primeiro INTEGER e o TEXT em um arquivo em um formato específico usando printf.
Problema: porque o TEXTO em algumas linhas tem espaços, o $ 3 não possui o TEXTO completo, portanto a linha é quebrada em mais campos.

Exemplo:

12 42956    Cinema - 3D/Multiplex  
7  12560    Status Update  
5  184   Movie  

Minha abordagem para isso é a seguinte:

awk '{ c=$3; for(i=4; i< NF;++i){c=c" "$i}; printf "<tag>%d</tag>\n<tag>%s</tag>\n", $1,c}';  

Mas achei que poderia haver uma abordagem melhor

    
por Jim 09.02.2017 / 10:35

5 respostas

1

awk é útil se os dados vierem em registros bem designados. Esses dados não. No entanto, os dados estão no formato " integer stuff the_rest ", em que " integer " e " stuff " não terão espaços. Isso é exatamente o que o utilitário read gosta de ler. Ele irá ler palavras separadas por espaços em branco, quantas você der a variáveis para ler, e então colocará "o resto" da linha na última variável.

bash-4.4$ while read -r integer stuff the_rest; do printf '%d\t"%s"\n' "$integer" "$the_rest"; done <data
12      "Cinema - 3D/Multiplex"
7       "Status Update"
5       "Movie"

Ele remove automaticamente todos os espaços em branco.

    
por 10.02.2017 / 13:03
1

Para extrair campos com base em um padrão, perl geralmente é melhor que awk :

perl -lne '
  if (/^\s*(\d+)\s*\S+\s*(.*?)\s*$/) {
    print "<tag>$1</tag><tag>$2</tag>"
  }'

que na sua entrada dá:

<tag>12</tag><tag>Cinema - 3D/Multiplex</tag>
<tag>7</tag><tag>Status Update</tag>
<tag>5</tag><tag>Movie</tag>

Isso significa que você pode fazer coisas mais avançadas, como fazer uma codificação HTML adequada, se necessário, por exemplo:

perl -Mopen=locale -MHTML::Entities -lne '
  if (/^\s*(\d+)\s*\S+\s*(.*?)\s*$/) {
    print map {"<tag>" . encode_entities($_) . "</tag>"} $1, $2
  }'

Ou codificação XML:

perl -Mopen=locale -MXML::LibXML -lne '
  if (/^\s*(\d+)\s*\S+\s*(.*?)\s*$/) {
    print map {
      my $e = XML::LibXML::Element->new("tag");
      $e->appendText($_);
      $e->toString} $1, $2
  }'
    
por 10.02.2017 / 13:57
1

Substitua os $ 2 (que você não usa de qualquer maneira) por um caractere não utilizado (um que não exista em suas strings). Depois disso, basta fazer:

awk '{$2="+";print}' input-file.txt | awk -F "+" '{printf "<tag>%d</tag>\n<tag>%s</tag>\n",$1,$2}'

Acima, usei o sinal de mais "+" como separador.

Não é a solução mais elegante, mas é simples.

    
por 10.02.2017 / 13:25
0

Eu acho que você pode querer algo como

awk '{$2=""; print;}' input
    
por 10.02.2017 / 11:44
0

Se esse não for um arquivo grande e o texto estiver sempre no final, como alternativa, você pode considerar uma abordagem bash clássica como:

while IFS=' ' read -r int1 int2 text;do
#do your stuff
done <file

Como acontece com while - read, o último var $ text no comando read obterá todos os campos restantes como um campo.

Teste:

$ IFS=' ' read -r int1 int2 text <<<"10 5 some text here"
$ echo "$text"
some text here

O Bash durante a leitura pode ter um desempenho bastante lento em arquivos de dados grandes, mas você pode tentar o seu caso.

    
por 10.02.2017 / 13:24