Обернуть и отступить текст с помощью coreutils

Вопрос или проблема

Краткая версия

Я хотел бы создать табличное представление многострочного текста, аналогичное следующему:

все       Собрать все цели
документ  Создать документацию исходных файлов в подпапках
          `src` и `script`, и записать это в `man`
тест      Запустить модульные тесты

На данный момент мой ввод выглядит следующим образом, но это, конечно, можно изменить:

все---Собрать все цели
документ---Создать документацию исходных файлов в подпапках `src` и `script`, и записать это в `man`
тест---Запустить модульные тесты

Я пытался добиться этого с помощью комбинации awk и wrap/pr, но, хотя перенос строк работает, отступы не работают. Вот мой текущий подход:

…
| awk -F '---' "{ printf '%-10s %s\n', $1, $2 }" \
| fold -w $(($COLUMNS - 1)) -s

Это генерирует вывод

все       Собрать все цели
документ  Создать документацию исходных файлов в подпапках
`src` и `script`, и записать это в `man`
тест      Запустить модульные тесты

… другими словами, третья строка не отступает, как и ожидалось.

Как я могу отформатировать текст с заданной длиной переноса и заданной шириной висячего отступа? — Не меняя ничего другого в тексте. Бонус: это должно работать с UTF-8 и управляющими символами.


Справочная информация

Цель состоит в том, чтобы создать самодокументируемые Makefile. В результате логика форматирования и отображения кода должна быть небольшой, автономной и не зависеть от отдельно установленного программного обеспечения; в идеале, она должна работать на любой системе, которая может выполнять Makefile, поэтому я ограничиваю себя (чем-то близким к) coreutils.

Сказав это, я кратко попытался решить проблему с помощью groff, но это стало слишком сложным очень быстро (и версия groff на OS X старая и, похоже, не поддерживает UTF-8).

Исходная строка, которую я пытаюсь разобрать и отформатировать, выглядит примерно так:

## Собрать все цели
все: тест документ

## Запустить модульные тесты
тест:
    ./run-tests .

## создать документацию исходных файлов в подпапках `src` и `script`,
## и записать это в `man`
документ:
    ${MAKE} -C src документ
    ${MAKE} -C script документ

На данный момент это разбирается с помощью скрипта sed (см. ссылку для получения деталей), который игнорирует многострочные комментарии, прежде чем быть поданным в код форматирования, приведенный выше.

После команды fold подайте вывод в sed и замените начало строки на табуляцию. И вы можете управлять отступом с помощью команды ‘tabs’ перед этим:

tabs 5
echo "Очень длинная строка, которую я хочу свернуть на границе слова и также отступить" | fold -s -w 20 | sed -e "s|^|\t|g"
     Очень длинная строка
     которую я хочу свернуть
     на границе слова
     и также отступить

С gnu awk вы можете сделать что-то простое, подобное этому:

awk -F '---' '
{ gsub(/.{50,60} /,"&\n           ",$2)
  printf "%-10s %s\n", $1, $2 }'

Для более точной и детальной версии, которая обрабатывает длинные слова:

awk -F '---' '
{ printf "%-10s ", $1
  n = split($2,x," ")
  len = 11
  for(i=1;i<=n;i++){
   if(len+length(x[i])>=80){printf "\n           "; len = 11}
   printf "%s ",x[i]
   len += 1+length(x[i])
  }
  printf "\n"
}'

Вот более короткий ответ, который использует fold, а затем смещает его вывод на 11 пробелов. Чтобы увидеть, что он делает, добавьте -v или -x к конечному bash.

| sed 's:\(.*\)---\(.*\):printf "%-10s " "\1";fold -w '$(($COLUMNS - 11))' -s <<\\!|sed "1!s/^/           /"\n\2\n!\n:' | bash 

Я думаю, что более простым решением было бы использовать команду column.
Во-первых, небольшое изменение формата вашего исходного файла упростит задачу.

Я заменил вашу строку-разделитель --- на одну вертикальную линию (|), чтобы она выступала в качестве символа-разделителя, и добавил вторую строку с ведущей | (которая будет объяснена позже).

Итак, ваш новый входной файл должен выглядеть примерно так:

все|Собрать все цели
документ|Создать документацию исходных файлов в подпапках `src` и `script`, и записать это в `man`
|Попытаться использовать последовательности ROFF, чтобы сделать страницы `man` согласованными с общепринятыми стандартами
тест|Запустить модульные тесты

Если мы теперь пропустим все это через команду column (в частности column -s '|' -t -W 2 -c 60 в этом случае), мы получим следующий вывод:

все       Собрать все цели
документ  Создать документацию исходных файлов в подпапках
          `src` и `script`, и записать это в `man`
          Попытаться использовать последовательности ROFF, чтобы сделать страницы `man`
          согласованными с общепринятыми стандартами
тест      Запустить модульные тесты

Как вы видите, текст был перенесен и отступлен, при этом столбцы выровнены друг под другом.
Дополнительные строки, предваряемые символом-разделителем, будут помещены во второй столбец и также будут перенесены.
Это может быть полезно, когда вы хотите принудительно выполнить перенос строки в вашем описательном тексте.

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

Ответ или решение

Как обернуть и отформатировать текст с использованием Coreutils

В данной статье мы рассмотрим, как обернуть и отформатировать многострочный текст с помощью утилит Coreutils в Unix-подобных системах. Мы будем работать над созданием табличного представления текста, где текст будет автоматически оборачиваться и иметь отступы, не нарушая при этом предыдущих разделов текста. Это особенно актуально для самодокументирующихся Makefile.

Начальное представление задачи

Предположим, у вас есть следующее представление данных, где строки разделены строкой "---":

all---Build all targets
document---Create documentation of source files in the subfolders `src` and `script`, and write it to `man`
test---Run unit tests

Наша цель — представить это в более читабельном формате, подобном следующему:

all       Build all targets
document  Create documentation of source files in the subfolders
          `src` and `script`, and write it to `man`
test      Run unit tests

Решение с использованием awk и fold

Для начала рассмотрим подход с использованием awk и fold. Мы используем awk для отделения первой и второй части строки, а затем обрабатываем текст с учетом отступов.

echo "all---Build all targets
document---Create documentation of source files in the subfolders \`src\` and \`script\`, and write it to \`man\`
test---Run unit tests" | awk -F '---' '
{
  printf "%-10s ", $1
  n = split($2,x," ")
  len = 11
  for(i=1;i<=n;i++){
    if(len+length(x[i])>=80){printf "\n           "; len = 11}
    printf "%s ",x[i]
    len += 1+length(x[i])
  }
  printf "\n"
}'

Объяснение скрипта

  1. Используем -F '---' для разделения строки на две части: ключевое слово (например, all) и описание.
  2. Выводим ключевое слово с отступом 10 символов с помощью printf "%-10s ", $1.
  3. Используем split для деления строки описания на отдельные слова.
  4. Внутренний цикл проверяет, превышает ли длина строки лимит, и если превышает, выполняется перевод строки с необходимым отступом.
  5. Все слова выводятся обратно на экран.

Использование fold и sed

Альтернативным подходом будет использование fold и sed:

echo "all---Build all targets
document---Create documentation of source files in the subfolders \`src\` and \`script\`, and write it to \`man\`
test---Run unit tests" | sed 's/---/|/g' | column -s '|' -t | sed '2,$s/^/           /'

Объяснение:

  1. Мы заменяем разделитель --- на более простой |, что упрощает работу с column.
  2. Затем с помощью column мы создаем табличный вывод.
  3. Наконец, с помощью второго sed мы добавляем отступ к тексту, начиная со второго ряда.

Работа с UTF-8 и управлением символами

Все предложенные решения корректно обрабатывают UTF-8. Убедитесь, что входные данные также используют кодировку UTF-8, чтобы избежать проблем с отображением символов.

Заключение

При решении задачи форматирования текста с использованием утилит Coreutils важно учитывать простоту и читабельность. Приведённые примеры показывают, как можно эффективно обернуть и отформатировать текст, чтобы сделать его удобным для чтения и впечатляющим, что полезно для целей самодокументирования в Makefile. Использование таких инструментов, как awk, fold и sed, позволяет добиваться желаемых результатов без необходимости в сложных библиотеках или внешних инструментах.

Оцените материал
Добавить комментарий

Капча загружается...