Форматирование вывода с помощью printf: усечение или заполнение

Я хотел бы произвести следующий вывод:

> Avril Stewart  99  54
> Sally Kinghorn 170 60
> John Young     195 120
> Yutte Schim... 250 40

Как видите, имена короче 14 символов дополняются пробелами. Имена длиннее 15 символов усекаются: "Yutte Schimmelpenninck" усекается до "Yutte Schim...".

Вот то, что я пытался добиться этого (переменные $name, $height, а также $weight извлекаются из файлов, и цикл запускает printf команда на каждый файл данных):

printf '%-14s -%3s -%3s\n' "$name" "$height" "$weight"
> Avril Stewart  99  54
> Sally Kinghorn 170 60
> John Young     195 120
> Yutte Schimmelpenninck 250 40

printf Однострочник - желаемое решение.

Какой код выдаст первый блок вывода?

3 ответа

Решение

Перед вашим printf Команда, вы хотите проверить, если имя длиннее 14 символов, и если да, обрежьте его и замените последние три символа точками. Эта команда делает это:

(( ${#name} > 14 )) && name="${name:0:11}..."

Заменяет name с его первыми одиннадцатью символами и дополнениями ...,

Вы также должны исправить printf строка формата: вместо

'%-14s -%3s -%3s\n'

должно быть

'%-14s %-3s -%-3s\n'

или вы получите такие результаты, как

Avril Stewart  - 99 - 54

Возможно, это была просто опечатка, поскольку в выходных данных вашего примера не было дефисов.

В общем:

$ name='Avril Stewart'; weight=99; height=54
$ (( ${#name} > 14 )) && name="${name:0:11}..."
$ printf '%-14s %-3s %-3s\n' "$name" $weight $height
Avril Stewart  99  54
$ name='Sally Kinghorn'; weight=170; height=60
$ (( ${#name} > 14 )) && name="${name:0:11}..."
$ printf '%-14s %-3s %-3s\n' "$name" $weight $height
Sally Kinghorn 170 60
$ name='Yutte Schimmelpeninck'; weight=250; height=40
$ (( ${#name} > 14 )) && name="${name:0:11}..."
$ printf '%-14s %-3s %-3s\n' "$name" $weight $height
Yutte Schim... 250 40

Так что, если вы прочитаете это из файла, например, через запятую, вы получите цикл вроде этого:

while IFS=, read -r name weight height; do
    (( ${#name} > 14 )) && name="${name:0:11}..."
    printf '%-14s %-3s %-3s\n' "$name" $weight $height
done < inputFile

в результате чего

Avril Stewart  99  54
Sally Kinghorn 170 60
John Young     195 120
Yutte Schim... 250 40

Я не думаю, что это возможно в одной строке. Я экспериментировал с троичным оператором и пробовал что-то вроде

printf '%s\n' $(( ${#name} > 14 ? "${name:0:11}..." : "$name" ))

но проблема здесь в том, что он работает только для целых чисел, а строки расширяются до нуля в арифметическом контексте.

Более идиоматичный awk версия будет

$ awk        '{v=$1 FS $2} 
  length(v)>14{v=substr(v,1,11)"..."}
              {printf "%-15s %-3d %-3d\n",v,$3,$4}' file     

Avril Stewart  99  54 
Sally Kinghorn 170 60 
John Young     195 120
Yutte Schim... 250 40

если вы удалите - войдите в%-3d, числа будут выровнены по правому краю, как обычно, однако это тот формат, который вы просили.

Это частичный ответ - игнорирование финального "...":

awk '{printf "%-14.14s %-3d %-3d\n",$1 FS $2,$3,$4}'

где %-14.14s выполняет заполнение + усечение форматирования имени

Другие вопросы по тегам