ГЛАВНАЯ RU typewriter

older-tomato

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

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

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

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

#!/bin/bash
# отсортированный список файлов и каталогов
function list_directory_contents {
  # сначала заглавные буквы, потом строчные, сначала каталоги, потом файлы
  eval "LC_COLLATE=C ls -A --group-directories-first $exclusions $1"
}
# дерево каталогов со ссылками
function directory_tree {
  # аргументы
  local path="$1"
  local head="$2"
  local tail="$3"
  # префикс для текущего элемента
  if [ "one" == "$4" ]; then
    echo -n "/"
  else
    echo -ne "\n$head"
  fi
  # текущий элемент дерева
  echo -n "<a href='${path#*/}'>${path##*/}</a>"
  # рекурсивные вызовы для подкаталогов
  if [ -d "$path" ]; then
    local list # массив файлов и каталогов
    readarray -t list <<<"$(list_directory_contents "$path")"
    local size=${#list[@]} # длина массива
    local i # счётчик
    for ((i = 0; i < size; i++)); do
      if [ -z "${list[$i]}" ]; then
        continue # пропустить пустой каталог
      elif ((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' ' ')"
# помещаем дерево в контейнер, добавляем заголовок и выводим в файл
{
  echo "## Дерево каталогов"
  echo -ne "\n<pre>"
  directory_tree .
  echo -e "\n</pre>"
} >DIRECTORY_TREE.md

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


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