Files
linux_tools/finder.sh
2026-06-01 02:03:00 +03:00

332 lines
12 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# Флаги
VERBOSE=0
SHOW_PATHS=0
RECURSIVE=1
TARGET_EXT=""
INPUT_DIR="."
NO_DEFAULT_EXCLUDES=0
EXCLUDE_DIRS=()
EXCLUDE_EXTS=()
PROGRESS_BAR=1 # новый флаг
# Функция помощи
show_help() {
echo "Использование: $0 [OPTIONS] [EXTENSION]"
echo ""
echo "Опции:"
echo " -i, --input DIR Входная директория (по умолчанию текущая)"
echo " -v, --verbose Подробный вывод"
echo " -p, --paths Показать пути к файлам (вместо статистики)"
echo " -n, --no-recursive Только текущая директория"
echo " -r, --recursive Рекурсивный обход (по умолчанию)"
echo " -e, --exclude-dir DIR Исключить директорию (можно несколько раз)"
echo " --exclude-ext EXT Исключить расширение файла (можно несколько раз)"
echo " -D, --no-default-excludes Не исключать .git (по умолчанию .git исключается)"
echo " --no-progress Отключить прогресс-бар"
echo " -h, --help Показать эту справку"
echo ""
echo "Примеры:"
echo " $0 # Статистика всех файлов"
echo " $0 .txt # Статистика txt файлов"
echo " $0 -p .py # Показать все пути к .py файлам"
echo " $0 -p # Показать пути ко всем файлам"
echo " $0 -v # Подробная статистика всех файлов"
echo " $0 -n # Только в текущей директории"
echo " $0 -e build --exclude-ext log # Исключить папку build и .log файлы"
echo " $0 -D # Отключить встроенное исключение .git"
echo " $0 -i ~/projects -p .py # Показать .py файлы в указанной папке"
echo " $0 --no-progress # Без прогресс-бара"
}
# Парсинг аргументов
while [[ $# -gt 0 ]]; do
case $1 in
-v|--verbose)
VERBOSE=1
shift
;;
-p|--paths)
SHOW_PATHS=1
shift
;;
-r|--recursive)
RECURSIVE=1
shift
;;
-n|--no-recursive)
RECURSIVE=0
shift
;;
-i|--input)
if [[ -z "$2" ]]; then
echo "Ошибка: опция -i требует аргумент"
exit 1
fi
INPUT_DIR="$2"
shift 2
;;
-e|--exclude-dir)
if [[ -z "$2" ]]; then
echo "Ошибка: опция -e требует аргумент"
exit 1
fi
EXCLUDE_DIRS+=("$2")
shift 2
;;
--exclude-ext)
if [[ -z "$2" ]]; then
echo "Ошибка: опция --exclude-ext требует аргумент"
exit 1
fi
EXCLUDE_EXTS+=("$2")
shift 2
;;
-D|--no-default-excludes)
NO_DEFAULT_EXCLUDES=1
shift
;;
--no-progress)
PROGRESS_BAR=0
shift
;;
-h|--help)
show_help
exit 0
;;
-*)
echo "Ошибка: Неизвестная опция $1"
show_help
exit 1
;;
*)
TARGET_EXT="$1"
shift
;;
esac
done
# Проверка существования входной директории
if [[ ! -d "$INPUT_DIR" ]]; then
echo "Ошибка: Директория '$INPUT_DIR' не существует"
exit 1
fi
# Переход в рабочую директорию
CURRENT_DIR="$(pwd)"
cd "$INPUT_DIR" || {
echo "Ошибка: Не удалось перейти в директорию '$INPUT_DIR'"
exit 1
}
WORK_DIR="$(pwd)"
# Очистка расширения: убираем ведущую точку, если есть
clean_ext() {
local ext="$1"
ext="${ext#.}"
echo "$ext"
}
# Функция получения расширения файла (без точки)
get_extension() {
local file="$1"
local filename=$(basename -- "$file")
local ext="${filename##*.}"
if [[ "$ext" == "$filename" ]]; then
echo "no_extension"
else
echo "$ext"
fi
}
# Функция отображения прогресс-бара (как в первой версии)
show_progress() {
local current=$1
local total=$2
local width=50
local percentage=$((current * 100 / total))
local completed=$((current * width / total))
local remaining=$((width - completed))
if [[ $total -eq 0 ]]; then
percentage=0
completed=0
remaining=$width
fi
printf "\r["
printf "%${completed}s" | tr ' ' '='
printf "%${remaining}s" | tr ' ' ' '
printf "] %3d%% Обработано: %d/%d" "$percentage" "$current" "$total"
if [[ $current -eq $total ]] && [[ $total -gt 0 ]]; then
echo ""
fi
}
# Основная функция поиска и вывода
process_files() {
# Построение массива аргументов для find
local find_args=(".")
local has_target=0
if [[ $RECURSIVE -eq 0 ]]; then
find_args+=("-maxdepth" "1")
fi
find_args+=("-type" "f")
if [[ $NO_DEFAULT_EXCLUDES -eq 0 ]]; then
find_args+=("-not" "-path" "./.git/*" "-not" "-path" "./.git")
fi
for dir in "${EXCLUDE_DIRS[@]}"; do
dir="${dir%/}"
local clean_dir="${dir#./}"
find_args+=("-not" "-path" "./$clean_dir" "-not" "-path" "./$clean_dir/*")
find_args+=("-not" "-path" "*/$clean_dir/*" "-not" "-path" "*/$clean_dir")
done
if [[ -n "$TARGET_EXT" ]]; then
has_target=1
if [[ "$TARGET_EXT" == "no_extension" ]]; then
find_args+=("!" "-name" "*.*")
else
local ext_clean=$(clean_ext "$TARGET_EXT")
find_args+=("-name" "*.$ext_clean")
fi
fi
for ext in "${EXCLUDE_EXTS[@]}"; do
ext=$(clean_ext "$ext")
find_args+=("-not" "-name" "*.$ext")
done
if [[ $VERBOSE -eq 1 ]]; then
echo "Рабочая директория: $WORK_DIR"
echo "Режим: $([[ $SHOW_PATHS -eq 1 ]] && echo 'Показ путей' || echo 'Статистика')"
echo "Рекурсивно: $([[ $RECURSIVE -eq 1 ]] && echo 'Да' || echo 'Нет')"
echo "Стандартные исключения: $([[ $NO_DEFAULT_EXCLUDES -eq 0 ]] && echo '.git' || echo 'Нет')"
[[ -n "$TARGET_EXT" ]] && echo "Целевое расширение: $TARGET_EXT"
[[ ${#EXCLUDE_DIRS[@]} -gt 0 ]] && echo "Исключаемые папки: ${EXCLUDE_DIRS[*]}"
[[ ${#EXCLUDE_EXTS[@]} -gt 0 ]] && echo "Исключаемые расширения: ${EXCLUDE_EXTS[*]}"
[[ $PROGRESS_BAR -eq 1 ]] && echo "Прогресс-бар: включен"
echo ""
fi
# ------------------------------------------------------------------
# РЕЖИМ ПОКАЗА ПУТЕЙ (без прогресс-бара, как раньше)
# ------------------------------------------------------------------
if [[ $SHOW_PATHS -eq 1 ]]; then
if [[ -n "$TARGET_EXT" ]]; then
echo "Пути к .$(clean_ext "$TARGET_EXT") файлам:"
else
echo "Пути ко всем файлам:"
fi
echo "=========================="
local count=0
while IFS= read -r -d '' file; do
echo "${file#./}"
((count++))
done < <(find "${find_args[@]}" -print0 2>/dev/null)
echo "-------------------------"
echo "Найдено файлов: $count"
return
fi
# ------------------------------------------------------------------
# РЕЖИМ СТАТИСТИКИ
# ------------------------------------------------------------------
declare -A stats
local total_files=0
# Если прогресс-бар включён сначала собираем все файлы в массив,
# иначе работаем потоково (быстрее, без двойного прохода)
if [[ $PROGRESS_BAR -eq 1 ]]; then
echo "Сканирование файлов..."
# Собираем массив путей (используем mapfile, как в первой версии)
mapfile -d '' all_files < <(find "${find_args[@]}" -print0 2>/dev/null)
total_files=${#all_files[@]}
local processed=0
if [[ $total_files -gt 0 ]]; then
echo "Обработка файлов:"
fi
for file in "${all_files[@]}"; do
local ext
if [[ -n "$TARGET_EXT" ]]; then
ext=$(clean_ext "$TARGET_EXT")
else
ext=$(get_extension "$file")
fi
((stats["$ext"]++))
((processed++))
show_progress "$processed" "$total_files"
done
# После завершения прогресс-бара переводим строку (если файлы были)
if [[ $total_files -gt 0 ]]; then
echo ""
fi
else
# Потоковый режим (без прогресс-бара) оригинальное поведение
while IFS= read -r -d '' file; do
local ext
if [[ -n "$TARGET_EXT" ]]; then
ext=$(clean_ext "$TARGET_EXT")
else
ext=$(get_extension "$file")
fi
((stats["$ext"]++))
done < <(find "${find_args[@]}" -print0 2>/dev/null)
# Подсчитаем общее количество для вывода (перебираем значения)
for count in "${stats[@]}"; do
((total_files += count))
done
fi
# Вывод статистики
if [[ -n "$TARGET_EXT" ]]; then
local t=$(clean_ext "$TARGET_EXT")
echo "Статистика .$t файлов:"
echo "============================="
echo "$t: ${stats[$t]:-0}"
else
echo "Статистика файлов:"
echo "================="
# Сортировка расширений по убыванию количества
for ext in "${!stats[@]}"; do
printf "%s %d\n" "$ext" "${stats[$ext]}"
done | sort -k2 -nr | while read ext count; do
printf "%-15s %d\n" "$ext" "$count"
done
# Если прогресс-бар НЕ использовался, total_files уже посчитан в цикле выше.
# Если использовался, пересчитаем из stats (т.к. total_files уже есть, но можно перестраховаться)
if [[ $PROGRESS_BAR -eq 0 ]]; then
# total_files уже посчитан, но если по какой-то причине нет пересчитаем
if [[ $total_files -eq 0 ]]; then
for count in "${stats[@]}"; do
((total_files += count))
done
fi
else
total_files=0
for count in "${stats[@]}"; do
((total_files += count))
done
fi
echo "-------------------------"
echo "Всего файлов: $total_files"
echo "Уникальных расширений: ${#stats[@]}"
fi
}
# Запуск обработки
process_files
# Возврат в исходную директорию
if [[ "$CURRENT_DIR" != "$WORK_DIR" ]]; then
cd "$CURRENT_DIR" || exit 1
fi