ГЛАВНАЯ RU typewriter

older-tomato

Дерево каталогов со ссылками

Рекурсия • Обработка файлов • Вёб-навигация 03.08.2023

Напишем скрипт Bash для построения дерева каталогов для репозитория в файле Markdown. Будем использовать только встроенные средства Bash и базовое ПО Linux — ls, sed, tr и printf — без дополнительных программ. Полученный файл DIRECTORY_TREE.md пригодится в вёб-интерфейсе для навигации по объектам репозитория.

Создаём рекурсивную функцию и с её помощью обходим все файлы и каталоги репозитория, за исключением списка из .gitignore — строим структуру каталогов в форме дерева. Выводим элементы в виде ссылок <a>, сворачиваем папки с одним вложенным элементом в одну строку, помещаем собранное дерево в контейнер <pre> и добавляем заголовок — в результате получаем краткий и лаконичный файл Markdown со ссылками.

#!/bin/bash
# дерево каталогов со ссылками
function directory_tree {
  # аргументы
  local path="$1"
  local head="$2"
  local tail="$3"
  # получить содержимое каталога
  if [ -d "$path" ]; then
    # сначала заглавные буквы, потом строчные, сначала каталоги, потом файлы
    ls_sorted="LC_COLLATE=C ls -A --group-directories-first $exclusions $path"
    # отсортированный массив файлов и каталогов
    local list && readarray -t list < <(eval "$ls_sorted")
    # длина массива
    local size=${#list[@]}
    # пропустить пустой каталог
    [ "$size" == 0 ] && return
  fi
  # префикс текущего элемента, сворачивать синглтоны в одну строку
  [ "$4" == "one" ] && printf '%s' "/" || printf '\n%s' "$head"
  # текущий элемент дерева — относительная гиперссылка
  printf '%s' "<a href='${path#*/}'>${path##*/}</a>"
  # рекурсивные вызовы для подкаталогов
  if [ -d "$path" ]; then
    local i # счётчик
    for ((i = 0; i < size; i++)); do
      if ((size == 1)); then
        directory_tree "$path/${list[$i]}" "$tail" "$tail" "one"
      elif ((i < size - 1)); then
        directory_tree "$path/${list[$i]}" "$tail├─ " "$tail│  "
      else
        directory_tree "$path/${list[$i]}" "$tail└─ " "$tail   "
      fi
    done
  fi
}
# строка исключений для "ls" из списка неотслеживаемых файлов ".gitignore"
exclusions="-I'.git' $(sed -E "s|^(.*)$|-I'\1'|" .gitignore | tr '\n' ' ')"
# помещаем дерево в контейнер, добавляем заголовок и выводим в файл
printf '%s\n' "## Дерево каталогов" "" "<pre>" \
  "$(directory_tree . | grep '\S')" "</pre>" >DIRECTORY_TREE.md

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


© Головин Г.Г., Код с комментариями, 2023