332 lines
12 KiB
Bash
Executable File
332 lines
12 KiB
Bash
Executable File
#!/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
|