--- title: Дерево каталогов со ссылками description: Напишем скрипт Bash для построения дерева каталогов для репозитория в файле Markdown. Будем использовать только средства Bash и базовое ПО Linux без... sections: [Рекурсия,Обработка файлов,Вёб-навигация] tags: [linux,bash,markdown,html,структура,каталоги,файлы,ссылки,сортировка] canonical_url: /ru/2023/08/03/directory-tree.html url_translated: /en/2023/08/04/directory-tree.html title_translated: Directory tree with links date: 2023.08.03 --- Напишем скрипт Bash для построения дерева каталогов для репозитория в файле Markdown. Будем использовать только средства Bash и базовое ПО Linux — `ls`, `sed`, `tr` и `echo` — без дополнительных программ. Полученный файл [`DIRECTORY_TREE.md`]({{ site.homepage_url }} "{{ site.homepage_name }}") будем использовать в вёб-интерфейсе для навигации по объектам репозитория. Создаём рекурсивную функцию и с её помощью обходим все файлы и каталоги репозитория, за исключением списка из `.gitignore` — строим структуру каталогов в форме дерева. Выводим элементы в виде ссылок ``, сворачиваем папки с одним вложенным элементом в одну строку, помещаем собранное дерево в контейнер `
` и добавляем заголовок — в результате получаем
краткий и лаконичный файл Markdown со ссылками.

```bash
#!/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 "${path##*/}"
  # рекурсивные вызовы для подкаталогов
  if [ -d "$path" ]; then
    local list # массив файлов и каталогов
    readarray -t list <<<"$(list_directory_contents "$path")"
    local len=${#list[@]} # размер массива
    local i # счётчик
    for ((i = 0; i < len; i++)); do
      if [ -z "${list[$i]}" ]; then
        continue # пропустить пустой каталог
      elif ((len == 1)); then
        directory_tree "$path/${list[$i]}" "$tail" "$tail" "one"
      elif ((i < len - 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 's/^/ -I "/;s/$/"/' .gitignore | tr -d '\n')"
# помещаем дерево в контейнер, добавляем заголовок и выводим в файл
{
  echo "## Дерево каталогов"
  echo -ne "\n
"
  directory_tree .
  echo -e "\n
" } >DIRECTORY_TREE.md ``` Запускаем скрипт в корне репозитория и сохраняем полученный файл.