Modifique a largura da primeira coluna no arquivo com um número variável de campos, usando awk

10

Entendo como usar a função printf do awk, mas não quero especificar todos os campos.

Por exemplo, suponha que este seja o meu arquivo:

c1|c2|c3|c4|c5
c6|c7|c8|c9|c10
c11|c12|c13|c14|c15

Quero formatá-lo para que o primeiro campo de cada registro tenha a largura de c11 - a célula mais longa no primeiro campo:

c1 |c2|c3|c4|c5
c6 |c7|c8|c9|c10
c11|c12|c13|c14|c15

Entendo que eu poderia especificar:

awk -F"|" '{printf "%-3s%s%s%s%s\n", $1, $2, $3, $4, $5}' file > newfile

Vamos supor que eu sei o que quero que a largura da primeira coluna seja, mas NÃO sei quantos campos estão no arquivo. Basicamente, quero fazer algo como:

... '{printf "%-3s|", $1}'

... e imprima o restante dos campos no formato original.

Kayli O'Keefe
fonte
Outra maneira de enfrentá-lo: sed 's/|/'' '' '' |/;s/\(...\) */\1/'(aqui adicionar aspas extras para inserir esses 3 espaços como os comentários SE espremer espaços contíguos em um)
Stéphane Chazelas

Respostas:

14

Você pode usar sprintf para reformatar $1apenas.

Ex.

$ awk 'BEGIN{OFS=FS="|"} {$1 = sprintf("%-3s",$1)} 1' file
c1 |c2|c3|c4|c5
c6 |c7|c8|c9|c10
c11|c12|c13|c14|c15
chave de aço
fonte
Conciso, você também pode usar a formatação dinâmica com o sprintf: Por exemploawk -vf1=3 'BEGIN{OFS=FS="|"}{$1=sprintf("%-*s",f1,$1)}1' test.txt
A.Danischewski
@ A.Danischewski - Bem, droga. Eu tenho feito uma extensa programação awk por ~ 17 anos e nunca me deparei com isso antes. Pensar em todos os aborrecimentos que teria me salvado.
Paul Sinclair
6

Para descobrir o comprimento maior / maior do primeiro campo e reformatar os valores no campo de acordo com esse comprimento, você precisará fazer duas passagens separadas sobre o arquivo.

awk 'BEGIN     { OFS = FS = "|" }
     FNR == NR { if (m < (n=length($1))) m = n; next }
               { $1 = sprintf("%-*s", m, $1); print }' file file

(observe que o arquivo de entrada é especificado duas vezes na linha de comando)

Para os dados que você apresenta, isso produziria

c1 |c2|c3|c4|c5
c6 |c7|c8|c9|c10
c11|c12|c13|c14|c15

A primeira passagem é tratada pelo FNR == NRbloco, que simplesmente acompanha o campo mais longo visto até agora (m contém o comprimento máximo visto) e pula para a próxima linha.

A segunda passagem é tratada pelo último bloco, que reformata o primeiro campo usando sprintf(). A string de formato%-*s significa "uma string justificada à esquerda cuja largura é fornecida pelo argumento inteiro antes do argumento que contém a string real".

Obviamente, isso poderia ser expandido para todas as colunas, transformando o escalar mem uma matriz que contém a largura máxima de cada coluna:

$ awk 'BEGIN     { OFS = FS = "|" }
       FNR == NR { for (i=1; i<=NF; ++i) if (m[i] < (n=length($i))) m[i] = n; next }
                 { for (i=1; i<=NF; ++i) $i = sprintf("%-*s", m[i], $i); print }' file file
c1 |c2 |c3 |c4 |c5
c6 |c7 |c8 |c9 |c10
c11|c12|c13|c14|c15
Kusalananda
fonte
1

A maneira inteligente é o que a chave de aço sugeriu . A maneira desnecessariamente complicada é iterar sobre todos os campos:

$ awk -F'|' '{printf "%-3s|",$1; for(i=2;i<NF;i++){printf "%s|",$i} printf "%s\n", $i}' file
c1 |c2|c3|c4|c5
c6 |c7|c8|c9|c10
c11|c12|c13|c14|c15

Mas apenas sprintf $1e termine com isso.

terdon
fonte
1
Você entendeu um pouco, as pequenas declarações concisas geralmente são mais complicadas. A iteração sobre os campos é menos complicada.
A.Danischewski 11/11/19
1

No Awk, você pode usar um "*" para gerar uma sequência de formato de impressão dinâmica.

Se você já conhece o comprimento, pode passar o comprimento do campo para a primeira coluna com -v.

awk -vcol1=3 'BEGIN{FS="|"}{for(i=1;i<=NF;i++){if(i==1)printf "%*-s%s",col1,$i,FS;else if(i!=NF)printf "%s%s",$i,FS;else printf "%s\n",$i;};}' test.txt

Nota: se você não sabia qual era o tamanho da primeira coluna, poderia armazenar os valores em uma matriz, encontrando o comprimento máximo da coluna ao longo do caminho e imprimindo tudo no bloco END.

A.Danischewski
fonte