Modifier la largeur de la première colonne du fichier avec un nombre variable de champs, en utilisant awk

10

Je comprends comment utiliser la fonction printf de awk, mais je ne veux pas spécifier chaque champ.

Par exemple, supposons qu'il s'agit de mon fichier:

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

Je veux le formater pour que le premier champ de chaque enregistrement soit la largeur de c11 - la cellule la plus longue du premier champ:

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

Je comprends que je pourrais préciser:

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

Supposons que je sais ce que je veux que la largeur de la première colonne soit, mais je ne sais PAS combien de champs sont dans le fichier. Fondamentalement, je veux faire quelque chose comme:

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

... puis imprimez le reste des champs dans leur format d'origine.

Kayli O'Keefe
la source
Une autre façon de le résoudre: sed 's/|/'' '' '' |/;s/\(...\) */\1/'(ici en ajoutant des guillemets supplémentaires pour insérer ces 3 espaces alors que les commentaires SE pressent les espaces contigus en un seul)
Stéphane Chazelas

Réponses:

14

Vous pouvez utiliser sprintfpour reformater $1uniquement.

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
tournevis
la source
Concis, vous pouvez également utiliser la mise en forme dynamique avec sprintf: par exempleawk -vf1=3 'BEGIN{OFS=FS="|"}{$1=sprintf("%-*s",f1,$1)}1' test.txt
A.Danischewski
@ A.Danischewski - Eh bien, dang. Je fais de la programmation awk étendue depuis environ 17 ans, et je n'ai jamais rencontré celui-là auparavant. Penser à tous les tracas que cela m'aurait sauvé.
Paul Sinclair
6

Pour déterminer la longueur la plus grande / la plus longue du premier champ, puis pour reformater les valeurs du champ en fonction de cette longueur, vous devrez effectuer deux passes distinctes sur le fichier.

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

(notez que le fichier d'entrée est spécifié deux fois sur la ligne de commande)

Pour les données que vous présentez, cela produirait

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

La première passe est gérée par le FNR == NRbloc, qui garde simplement la trace du champ le plus long vu jusqu'à présent ( mcontient la longueur maximale vue) et passe à la ligne suivante.

La deuxième passe est gérée par le dernier bloc, qui reformate le premier champ en utilisant sprintf(). La chaîne de format %-*ssignifie "une chaîne justifiée à gauche dont la largeur est donnée par l'argument entier avant l'argument qui contient la chaîne réelle".

Cela pourrait évidemment être étendu pour faire toutes les colonnes en transformant le scalaire men un tableau qui contient la largeur maximale de chaque colonne:

$ 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
la source
1

La manière intelligente est ce que propose Steeldriver . La façon alambiquée inutilement est d'itérer sur chaque champ:

$ 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

Mais juste sprintf $1et en finir avec ça.

terdon
la source
1
Vous l'avez un peu en arrière, les petites déclarations concises sont généralement plus compliquées. Itérer sur les champs est moins compliqué.
A.Danischewski
1

Dans Awk, vous pouvez utiliser un "*" pour générer une chaîne de format printf dynamique.

Si vous connaissez déjà la longueur, vous pouvez passer la longueur du champ pour la première colonne avec -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

Remarque: si vous ne saviez pas quelle est la longueur de la première colonne, vous pouvez stocker les valeurs dans un tableau, puis trouver la longueur maximale des colonnes en cours de route et l'imprimer dans le bloc END.

A.Danischewski
la source