Изменить ширину первого столбца в файле с переменным количеством полей, используя awk

10

Я понимаю, как использовать функцию printf в awk, но я не хочу указывать каждое поле.

Например, предположим, что это мой файл:

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

Я хочу отформатировать его так, чтобы первое поле каждой записи было шириной c11 - самой длинной ячейки в первом поле:

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

Я понимаю, что я мог бы указать:

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

Давайте предположим, что я знаю, какой должна быть ширина первого столбца, но я НЕ знаю, сколько полей в файле. В основном я хочу сделать что-то вроде:

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

... а затем распечатайте остальные поля в их исходном формате.

Кейли О'Киф
источник
Другой способ решения этой проблемы: sed 's/|/'' '' '' |/;s/\(...\) */\1/'(здесь добавление дополнительных кавычек для вставки этих трех пробелов, поскольку комментарии SE сжимают непрерывные пробелы в одну)
Стефан Шазелас,

Ответы:

14

Вы можете использовать только sprintfдля переформатирования $1.

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
steeldriver
источник
Сжатый, вы можете использовать динамическое форматирование с Sprintf тоже: Евawk -vf1=3 'BEGIN{OFS=FS="|"}{$1=sprintf("%-*s",f1,$1)}1' test.txt
A.Danischewski
@ А.Данищевски - Черт возьми. Я занимался обширным awk-программированием в течение ~ 17 лет, и никогда раньше не сталкивался с этим. Думать обо всех неприятностях, это спасло бы меня.
Пол Синклер
6

Чтобы определить наибольшую / самую длинную длину первого поля, а затем переформатировать значения в поле в соответствии с этой длиной, вам придется выполнить два отдельных прохода по файлу.

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

(обратите внимание, что входной файл указан дважды в командной строке)

Для данных, которые вы предоставляете, это даст

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

Первый проход обрабатывается FNR == NRблоком, который просто отслеживает самое длинное видимое поле ( mсодержит максимальную видимую длину) и переходит к следующей строке.

Второй проход обрабатывается последним блоком, который форматирует первое поле, используя sprintf(). Строка формата %-*sозначает «выровненную по левому краю строку, ширина которой задается целочисленным аргументом перед аргументом, который содержит фактическую строку».

Очевидно, это можно расширить, чтобы сделать все столбцы, превратив скаляр mв массив, который содержит максимальную ширину каждого столбца:

$ 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
Кусалананда
источник
1

Интеллектуальный способ - это то, что предложил стилдрайвер . Излишне запутанный способ - перебирать все поля:

$ 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

Но просто sprintf $1и покончим с этим.

Тердон
источник
1
Вы получаете это немного задом наперед, небольшие краткие заявления, как правило, более запутанные. Итерации по полям менее запутанны.
А.Данищевский
1

В Awk вы можете использовать «*» для генерации динамической строки формата printf.

Если вы уже знаете длину, вы можете передать длину поля для первого столбца с -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

Примечание: если вы не знали, какова длина первого столбца, вы можете сохранить значения в массиве, а затем найти максимальную длину столбца и распечатать все это в блоке END.

A.Danischewski
источник