ВАРИАНТ 2 Скрипт все тоже самое что описано выше отсутствует функция создания и работы с папкой organize
ОБЯЗАТЕЛЬНО ПРОПИСАТЬ ПУТЬ ДО ПАПКИ В УТИЛИТЕ EPSON SCAN НАПРИМЕР доки из архивов/EPSON и указать имя файла сопровод по окончании сканирования этот фаил всегда будет создаваться с именем сопровод.pdfСоздайте фаил в программе Kwrite c именем menu.sh и добавьте в его содержание следующий код:
#!/bin/bash
# ==========================================
# НАСТРОЙКИ И ПЕРЕМЕННЫЕ
# ==========================================
BASE_DIR="$HOME/Рабочий стол/Доки из архивов"
# Цвета для красоты
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
log() { echo -e "${GREEN}[INFO]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
# Проверка наличия ghostscript
check_gs() {
if ! command -v gs &> /dev/null; then
echo ""
error "Ghostscript не установлен! Без него сжатие PDF невозможно."
echo "Установите командой: sudo apt-get install ghostscript"
echo ""
return 1
fi
return 0
}
# ==========================================
# ПОДМЕНЮ: СЖАТИЕ PDF (ВСТРОЕННОЕ)
# ==========================================
submenu_pdf_compress() {
local pdf_dir="$BASE_DIR/Compress"
local pdf_files=()
local selected_files=()
if [[ ! -d "$pdf_dir" ]]; then
error "Папка Compress не найдена: $pdf_dir. Сначала создайте её через пункт 3 главного меню."
read -p "Нажмите Enter, чтобы вернуться..." _
return 1
fi
find_pdf_files() {
pdf_files=()
local i=1
for file in "$pdf_dir"/*.pdf; do
if [[ -f "$file" ]]; then
pdf_files+=("$file")
echo " $i) $(basename "$file")"
((i++))
fi
done
if [[ ${#pdf_files[@]} -eq 0 ]]; then
echo " В папке Compress нет PDF-файлов."
else
echo " Найдено PDF-файлов: ${#pdf_files[@]}"
fi
}
compress_one() {
local input_file="$1"
local output_file="$pdf_dir/compressed_$(basename "$input_file")"
echo "Сжимаю: $(basename "$input_file")..."
gs -sDEVICE=pdfwrite \
-dCompatibilityLevel=1.4 \
-dPDFSETTINGS=/ebook \
-dNOPAUSE -dQUIET -dBATCH \
-sOutputFile="$output_file" \
"$input_file"
if [[ $? -eq 0 ]]; then
local original_size=$(stat -c%s "$input_file")
local compressed_size=$(stat -c%s "$output_file")
local savings=$((original_size - compressed_size))
if command -v bc &> /dev/null; then
local percent
percent=$(echo "scale=2; $savings * 100 / $original_size" | bc -l 2>/dev/null || echo 0)
printf " ✓ Успешно: экономия %d байт (%.1f%%)\n" "$savings" "$percent"
else
printf " ✓ Успешно: экономия %d байт\n" "$savings"
fi
else
echo " ✗ Ошибка при сжатии $(basename "$input_file")"
rm -f "$output_file"
fi
}
delete_files() {
local to_delete=("$@")
if [[ ${#to_delete[@]} -eq 0 ]]; then
echo "Ничего не выбрано для удаления."
return
fi
echo ""
echo "⚠️ ВНИМАНИЕ: Будут удалены следующие файлы:"
for f in "${to_delete[@]}"; do
echo " - $(basename "$f")"
done
echo ""
read -p "Вы уверены? Введите 'ДА' (или y) для подтверждения, любой другой текст для отмены: " confirm
if [[ "$confirm" == "ДА" || "$confirm" == "да" || "$confirm" == "y" || "$confirm" == "Y" ]]; then
for f in "${to_delete[@]}"; do
if [[ -f "$f" ]]; then
rm -f "$f"
log "Удален: $(basename "$f")"
fi
done
echo ""
echo "✅ Файлы удалены!"
else
echo "Удаление отменено."
fi
read -p "Нажмите Enter, чтобы продолжить..." _
}
while true; do
clear
echo ""
echo "=========================================="
echo " МЕНЮ СЖАТИЯ PDF-ФАЙЛОВ"
echo "=========================================="
echo "1) Сжать ВСЕ файлы в папке Compress"
echo "2) Выбрать конкретные файлы по номеру для сжатия"
echo "3) Удалить несжатые файлы (выбор по списку)"
echo "4) Назад в главное меню"
echo "=========================================="
read -p "Выберите пункт (1-4): " choice
case "$choice" in
1)
echo ""
echo "Список файлов для сжатия:"
find_pdf_files
if [[ ${#pdf_files[@]} -eq 0 ]]; then
echo ""
read -p "Нет файлов. Нажмите Enter, чтобы вернуться..." _
continue
fi
echo ""
echo "Начинаю сжатие всех файлов..."
echo "------------------------------------------"
for file in "${pdf_files[@]}"; do
compress_one "$file"
done
echo ""
echo "✅ Все файлы сжаты!"
read -p "Нажмите Enter, чтобы продолжить..." _
;;
2)
echo ""
echo "Сначала покажем список файлов:"
find_pdf_files
if [[ ${#pdf_files[@]} -eq 0 ]]; then
echo ""
read -p "Нет файлов для выбора. Нажмите Enter..." _
continue
fi
echo ""
echo "Введите номер(а) файла(ов) через пробел (например: 1 3 5)."
echo "Чтобы отменить, просто нажмите Enter без ввода."
read -r selection
if [[ -z "$selection" ]]; then
echo "Отмена."
read -p "Нажмите Enter..." _
continue
fi
selected_files=()
local error=0
for num in $selection; do
if ! [[ "$num" =~ ^[0-9]+$ ]]; then
echo "❌ Ошибка: '$num' — не номер."
error=1
break
fi
if [[ "$num" -lt 1 || "$num" -gt "${#pdf_files[@]}" ]]; then
echo "❌ Ошибка: номер '$num' вне диапазона (1-${#pdf_files[@]})."
error=1
break
fi
selected_files+=("${pdf_files[$((num-1))]}")
done
if [[ $error -eq 1 ]]; then
read -p "Нажмите Enter, чтобы попробовать снова..." _
continue
fi
if [[ ${#selected_files[@]} -eq 0 ]]; then
echo "Ничего не выбрано."
read -p "Нажмите Enter..." _
continue
fi
echo ""
echo "Выбрано: ${#selected_files[@]} файлов. Начинаю сжатие..."
echo "------------------------------------------"
for file in "${selected_files[@]}"; do
compress_one "$file"
done
echo ""
echo "✅ Выбранные файлы сжаты!"
read -p "Нажмите Enter, чтобы продолжить..." _
;;
3)
echo ""
echo "Сначала покажем список всех PDF в папке:"
find_pdf_files
if [[ ${#pdf_files[@]} -eq 0 ]]; then
echo ""
read -p "Нет файлов для удаления. Нажмите Enter..." _
continue
fi
echo ""
echo "Введите номера файлов, которые нужно УДАЛИТЬ (через пробел)."
echo "⚠️ Будьте осторожны: это действие необратимо!"
echo "Чтобы отменить, просто нажмите Enter."
read -r selection
if [[ -z "$selection" ]]; then
echo "Отмена удаления."
read -p "Нажмите Enter..." _
continue
fi
selected_files=()
local error=0
for num in $selection; do
if ! [[ "$num" =~ ^[0-9]+$ ]]; then
echo "❌ Ошибка: '$num' — не номер."
error=1
break
fi
if [[ "$num" -lt 1 || "$num" -gt "${#pdf_files[@]}" ]]; then
echo "❌ Ошибка: номер '$num' вне диапазона (1-${#pdf_files[@]})."
error=1
break
fi
selected_files+=("${pdf_files[$((num-1))]}")
done
if [[ $error -eq 1 ]]; then
read -p "Нажмите Enter, чтобы попробовать снова..." _
continue
fi
if [[ ${#selected_files[@]} -eq 0 ]]; then
echo "Ничего не выбрано."
read -p "Нажмите Enter..." _
continue
fi
delete_files "${selected_files[@]}"
;;
4)
echo "Возврат в главное меню."
break
;;
*)
echo "Неверный выбор. Введите 1, 2, 3 или 4."
read -p "Нажмите Enter..." _
;;
esac
done
}
# ==========================================
# ГЛАВНОЕ МЕНЮ И ФУНКЦИИ
# ==========================================
show_main_menu() {
clear
echo ""
echo "=== МЕНЮ (Alt Basalt для работы с документами Делопроизводство) ==="
echo "1. Создать каталог «Доки из архивов» и папки 1–N (выбор количества)"
echo "2. Создать папку Epson внутри «Доки из архивов»"
echo "3. Создать папку Compress внутри «Доки из архивов»"
echo "4. Сжатие PDF (отдельное подменю)"
echo "5. Выполнить скрипт объединения jpg/bmp в единый PDF"
echo "6. Извлечь документы из архивов (zip/tar.gz/7z)"
echo "7. Переименовать файл сопровод.pdf в папке Epson"
echo "8. Удалить папки с номерами 1-6 (и выше) в «Доки из архивов»"
echo "9. Выбрать и скопировать спецсимволы"
echo "0. Выход"
echo ""
}
create_base_and_folders() {
log "Создание базовой папки: $BASE_DIR"
mkdir -p "$BASE_DIR" || { error "Не удалось создать папку! Проверьте права доступа."; return 1; }
read -p "Сколько папок (1-N) нужно создать внутри? (например, 5): " count
if ! [[ "$count" =~ ^[0-9]+$ ]] || [ "$count" -le 0 ]; then
warn "Некорректное число. Папки не созданы."
return
fi
for ((i=1; i<=count; i++)); do
mkdir -p "$BASE_DIR/$i"
log "Создана папка: $BASE_DIR/$i"
done
log "Готово!"
}
create_epson_folder() {
mkdir -p "$BASE_DIR/Epson"
log "Папка Epson создана: $BASE_DIR/Epson"
}
create_compress_folder() {
mkdir -p "$BASE_DIR/Compress"
log "Папка Compress создана: $BASE_DIR/Compress"
}
merge_images_to_pdf() {
local base_dir="$BASE_DIR"
# Проверка наличия python3
if ! command -v python3 &> /dev/null; then
error "Не найден python3! Без него этот пункт не работает."
read -p "Нажмите Enter, чтобы продолжить..." _
return 1
fi
python3 - "$base_dir" <<'PYEOF'
import sys, os, struct, zlib, io
class SimplePDF:
def __init__(self):
self.objects = []
def add_obj(self, content):
self.objects.append(content)
return len(self.objects)
def build(self):
buf = io.BytesIO()
# Заголовок PDF
buf.write(b"%PDF-1.4\n%\xE2\xE3\xCF\xD3\n")
offsets = []
for i, obj_content in enumerate(self.objects):
offsets.append(buf.tell())
buf.write(f"{i+1} 0 obj\n".encode('latin-1'))
buf.write(obj_content)
buf.write(b"\nendobj\n")
xref_start = buf.tell()
total_objs = len(self.objects) + 1
buf.write(f"xref\n0 {total_objs}\n".encode('latin-1'))
buf.write(b"0000000000 65535 f \n")
for off in offsets:
buf.write(f"{off:010d} 00000 n \n".encode('latin-1'))
buf.write(f"trailer\n<< /Size {total_objs} /Root 1 0 R >>\nstartxref\n{xref_start}\n%%EOF".encode('latin-1'))
return buf.getvalue()
def create_folder_pdf(image_paths, output_path):
pdf = SimplePDF()
# 1. Catalog
pdf.add_obj(b"<< /Type /Catalog /Pages 2 0 R >>")
# 2. Pages (placeholder)
pdf.add_obj(b"")
page_ids = []
for img_path in image_paths:
ext = os.path.splitext(img_path)[1].lower()
stream = None
filter_type = None
try:
# Читаем файл целиком как бинарные данные
with open(img_path, 'rb') as f:
stream = f.read()
if ext in ['.jpg', '.jpeg', '.jpe', '.jfif']:
# ВАЖНО: Мы НЕ читаем размеры!
# PDF сам поймет размеры из потока JPEG при рендере.
# Но для структуры PDF нам все равно нужно указать Width/Height.
# Так как мы не можем их надежно прочитать, мы используем хитрость:
# Вставляем картинку на страницу 100x100, а потом масштабируем её
# через матрицу трансформации, чтобы она влезла.
# НО! Самый простой способ для сканов — использовать оператор Do без явных размеров в MediaBox,
# а задать размеры страницы под картинку.
# Поскольку мы не знаем размеры, мы сделаем страницу "бесконечной" (условно большой)
# и положим картинку в угол. Большинство просмотрщиков покажут её корректно.
# Однако, стандарт PDF требует Width/Height в словаре изображения.
# Если мы их не укажем, PDF будет битым.
# ХАК ДЛЯ СКАНОВ: Попробуем прочитать размеры самым простым способом (первые байты)
# Если не выйдет — поставим заглушку 1000x1000. Для сканов это не критично,
# так как картинка будет масштабироваться.
w, h = 1000, 1000
try:
# Очень простая попытка найти SOF маркер
if len(stream) > 10:
pos = 2
while pos < len(stream) - 9:
if stream[pos] == 0xFF:
marker = stream[pos+1]
if marker in (0xC0, 0xC1, 0xC2, 0xC3, 0xC5, 0xC6, 0xC7, 0xC9, 0xCA, 0xCB, 0xCD, 0xCE, 0xCF):
h = struct.unpack('>H', stream[pos+5:pos+7])[0]
w = struct.unpack('>H', stream[pos+7:pos+9])[0]
break
pos += 1
except:
pass
filter_type = b"/DCTDecode"
elif ext == '.bmp':
# Для BMP тоже попробуем прочитать заголовок, если нет - заглушка
w, h = 1000, 1000
if len(stream) >= 26:
try:
width = struct.unpack_from('<i', stream, 18)[0]
height = abs(struct.unpack_from('<i', stream, 22)[0])
if width > 0 and height > 0:
w, h = width, height
except:
pass
# Конвертируем BMP в RGB и сжимаем
# (Упрощенная логика конвертации для надежности)
header_size = struct.unpack_from('<I', stream, 14)[0]
offset = struct.unpack_from('<I', stream, 10)[0]
planes = struct.unpack_from('<H', stream, 26)[0]
bit_count = struct.unpack_from('<H', stream, 28)[0]
compression = struct.unpack_from('<I', stream, 30)[0]
if planes == 1 and compression == 0 and bit_count == 24:
row_size = ((w * bit_count + 31) // 32) * 4
rgb = bytearray()
for y in range(h):
src_y = h - 1 - y # BMP хранится снизу вверх
row_offset = offset + src_y * row_size
for x in range(w):
off = row_offset + x * 3
b = stream[off]
g = stream[off+1]
r = stream[off+2]
rgb.append(r); rgb.append(g); rgb.append(b)
stream = zlib.compress(bytes(rgb), 6)
filter_type = b"/FlateDecode"
else:
print(f" ! Неподдерживаемый BMP формат: {os.path.basename(img_path)}", file=sys.stderr)
continue
else:
continue
# Создаем объект изображения
# Даже если размеры приблизительные (1000x1000), картинка отобразится,
# просто может потребоваться зум в просмотрщике.
img_hdr = f"<< /Type /XObject /Subtype /Image /Width {w} /Height {h} /ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter ".encode('latin-1')
img_hdr += filter_type + f" /Length {len(stream)} >>".encode('latin-1')
img_obj = img_hdr + b"\nstream\n" + stream + b"\nendstream"
img_id = pdf.add_obj(img_obj)
# Контент страницы: рисуем картинку на весь лист (масштабируем если нужно)
# Матрица: w 0 0 h 0 0 -> растягивает картинку на размер страницы [0 0 w h]
content_stream = f"q\n1 0 0 1 0 0 cm\n/Im1 Do\nQ".encode('latin-1')
cs_obj = f"<< /Length {len(content_stream)} >>\nstream\n".encode('latin-1') + content_stream + b"\nendstream"
cs_id = pdf.add_obj(cs_obj)
# Страница: MediaBox должен быть достаточно большим, чтобы вместить картинку
# Если размеры были заглушкой (1000), страница будет 1000x1000.
page_obj = f"<< /Type /Page /Parent 2 0 R /MediaBox [0 0 {w} {h}] /Contents {cs_id} 0 R /Resources << /XObject << /Im1 {img_id} 0 R >> >> >>".encode('latin-1')
page_id = pdf.add_obj(page_obj)
page_ids.append(page_id)
except Exception as e:
print(f" ! Критическая ошибка обработки {os.path.basename(img_path)}: {e}", file=sys.stderr)
continue
if not page_ids:
return False
kids_str = " ".join(f"{pid} 0 R" for pid in page_ids)
pages_obj = f"<< /Type /Pages /Kids [{kids_str}] /Count {len(page_ids)} >>".encode('latin-1')
pdf.objects[1] = pages_obj
with open(output_path, 'wb') as f:
f.write(pdf.build())
return True
# === ГЛАВНАЯ ЛОГИКА ===
base_dir = sys.argv[1]
if not os.path.isdir(base_dir):
print(f"Ошибка: папка {base_dir} не существует", file=sys.stderr)
sys.exit(1)
folders = []
for item in os.listdir(base_dir):
full_path = os.path.join(base_dir, item)
if os.path.isdir(full_path) and item.isdigit():
folders.append((int(item), full_path))
folders.sort(key=lambda x: x[0])
print("🔍 Поиск картинок и создание PDF по папкам:")
success_count = 0
for num, folder_path in folders:
images = []
for fname in os.listdir(folder_path):
if fname.lower().endswith(('.jpg', '.jpeg', '.jpe', '.jfif', '.bmp')):
images.append(os.path.join(folder_path, fname))
if not images:
print(f" ⚠️ Папка {num}: нет картинок")
continue
images.sort(key=os.path.basename)
print(f" ✅ Папка {num}: найдено {len(images)} файлов. Создаю PDF...")
out_pdf = os.path.join(folder_path, f"folder_{num}.pdf")
if create_folder_pdf(images, out_pdf):
size = os.path.getsize(out_pdf)
print(f" 📄 Готово: {out_pdf} ({size} байт)")
success_count += 1
else:
print(f" ❌ Ошибка: не удалось создать PDF для папки {num}")
if success_count == 0:
print("\n❌ Не создано ни одного PDF.")
sys.exit(1)
else:
print(f"\n🎉 Успешно создано PDF файлов: {success_count}")
PYEOF
read -p "Нажмите Enter, чтобы вернуться в меню..." _
}
extract_archives() {
local base_dir="$BASE_DIR"
# Папки, которые проверяем. Можно расширить до 10, 20 — как нужно.
local folders=(1 2 3 4 5 6)
local total_processed=0
local skipped=0
if [[ ! -d "$base_dir" ]]; then
warn "Папка $base_dir не найдена. Сначала создайте её через пункт 1 главного меню."
read -p "Нажмите Enter, чтобы вернуться..." _
return 1
fi
clear
echo ""
echo "=== РАСПАКОВКА С СОХРАНЕНИЕМ СТРУКТУРЫ + УДАЛЕНИЕМ АРХИВОВ ==="
echo "Папки внутри архивов сохраняются, архивы удаляются только при успехе."
echo "Рабочая папка: $base_dir"
echo "------------------------------------------"
for folder in "${folders[@]}"; do
local full_path="$base_dir/$folder"
if [[ ! -d "$full_path" ]]; then
warn "Папка '$folder' не найдена, пропускаем."
continue
fi
echo ""
log "Обрабатываем папку: $folder (путь: $full_path)"
# ---------------------------------------------------------
# ЭТАП 1: ZIP (.zip)
shopt -s nullglob
for zip_file in "$full_path"/*.zip "$full_path"/*.ZIP "$full_path"/*.Zip; do
[[ -f "$zip_file" ]] || continue
log "📦 ZIP: $(basename "$zip_file")"
# -o: перезаписать без вопросов, структуру папок сохраняем (без флага -j)
if unzip -o -d "$full_path" "$zip_file" > /dev/null 2>&1; then
# Распаковка успешна → удаляем архив
rm -f "$zip_file"
((total_processed++))
log "✅ Успешно: структура сохранена, архив удалён"
else
warn "⚠️ Ошибка ZIP — архив НЕ удалён (проверьте содержимое)"
((skipped++))
fi
done
shopt -u nullglob
# ---------------------------------------------------------
# ЭТАП 2: TAR.GZ / TGZ (.tar.gz, .tgz)
shopt -s nullglob
for tar_file in "$full_path"/*.tar.gz "$full_path"/*.tgz "$full_path"/*.TAR.GZ; do
[[ -f "$tar_file" ]] || continue
log "📦 TAR.GZ: $(basename "$tar_file")"
# Структура сохраняется автоматически, ничего дополнительно делать не надо
if tar -xzf "$tar_file" -C "$full_path"; then
rm -f "$tar_file"
((total_processed++))
log "✅ Успешно: структура сохранена, архив удалён"
else
warn "⚠️ Ошибка TAR.GZ — архив НЕ удалён"
((skipped++))
fi
done
shopt -u nullglob
# ---------------------------------------------------------
# ЭТАП 3: 7Z (.7z)
if command -v 7z &> /dev/null; then
shopt -s nullglob
for sevenz_file in "$full_path"/*.7z "$full_path"/*.7Z; do
[[ -f "$sevenz_file" ]] || continue
log "📦 7Z: $(basename "$sevenz_file")"
# -aoa: перезаписать все без вопросов
if 7z x -aoa -o"$full_path" "$sevenz_file" > /dev/null 2>&1; then
rm -f "$sevenz_file"
((total_processed++))
log "✅ Успешно: 7Z распакован, архив удалён"
else
warn "⚠️ Ошибка 7Z — архив НЕ удалён"
((skipped++))
fi
done
shopt -u nullglob
else
warn "Утилита '7z' не найдена — пропускаем архивы .7z"
fi
done
echo ""
echo "=========================================="
printf "📊 Результат:\n"
printf " ✅ Обработано и удалено: %d\n" "$total_processed"
if (( skipped > 0 )); then
printf " ⚠️ Ошибки (архивы сохранены): %d\n" "$skipped"
fi
echo "=========================================="
echo ""
echo "💡 Папки из архивов сохранены."
echo " Успешные архивы удалены, битые — оставлены для проверки."
read -p "Нажмите Enter, чтобы вернуться в меню..." _
}
rename_accompanying_file() {
local dir="$BASE_DIR/Epson"
local prefix_old="сопровод"
local prefix_new="приложение"
local max_count=15
if [[ ! -d "$dir" ]]; then
warn "Папка Epson не найдена: $dir"
read -p "Нажмите Enter, чтобы продолжить..." _
return 1
fi
while true; do
clear
echo ""
echo "=== ПЕРЕИМЕНОВАНИЕ В ПАПКЕ EPSON ==="
echo "1) Жёстко: сопровод.pdf → конверт.pdf"
echo "2) Пакетно: сохранить цифру из имени (сопроводX → приложениеX)"
echo "3) Очистить папку Epson (удалить ВСЕ файлы)"
echo "4) Назад в главное меню"
echo ""
read -p "Выберите пункт (1-4): " choice
case "$choice" in
1)
# Жёсткое переименование в конверт.pdf
local old_path="$dir/сопровод.pdf"
local new_path="$dir/конверт.pdf"
if [[ ! -f "$old_path" ]]; then
warn "Файл $old_path не найден."
read -p "Нажмите Enter..." _
continue
fi
if [[ -f "$new_path" ]]; then
error "Файл $new_path уже существует! Переименование отменено."
read -p "Нажмите Enter..." _
continue
fi
if mv "$old_path" "$new_path"; then
log "Переименовано: сопровод.pdf → конверт.pdf"
else
error "Не удалось переименовать файл."
fi
read -p "Нажмите Enter, чтобы вернуться..." _
;;
2)
# ПАКЕТНОЕ: логика "цифра из старого имени = цифра в новом"
local files=()
local names=()
# Ищем базовый файл без цифры
if [[ -f "$dir/${prefix_old}.pdf" ]]; then
files+=("$dir/${prefix_old}.pdf")
names+=("${prefix_old}.pdf")
fi
# Ищем файлы с цифрами 1..15
for ((i=1; i<=max_count; i++)); do
local fname="${prefix_old}${i}.pdf"
if [[ -f "$dir/$fname" ]]; then
files+=("$dir/$fname")
names+=("$fname")
fi
done
if [[ ${#files[@]} -eq 0 ]]; then
warn "В папке Epson нет файлов вида ${prefix_old}*.pdf"
read -p "Нажмите Enter, чтобы вернуться..." _
continue
fi
echo ""
echo "Список доступных файлов:"
for i in "${!names[@]}"; do
printf " %d) %s\n" "$((i+1))" "${names[i]}"
done
echo ""
read -p "Введите номера файлов для переименования (через пробел): " selection
if [[ -z "$selection" ]]; then
echo "Отмена."
read -p "Нажмите Enter..." _
continue
fi
local selected_indices=()
for num in $selection; do
# Валидация номера
if [[ "$num" =~ ^[0-9]+$ ]] && (( num >= 1 && num <= ${#files[@]} )); then
selected_indices+=("$num")
else
warn "Пропущен неверный номер: '$num'"
fi
done
if [[ ${#selected_indices[@]} -eq 0 ]]; then
echo "Ничего не выбрано для обработки."
read -p "Нажмите Enter..." _
continue
fi
echo ""
echo "Начинаю обработку выбранных файлов..."
echo "------------------------------------------"
local success_count=0
local skip_count=0
for idx in "${selected_indices[@]}"; do
local real_idx=$((idx - 1))
local old_full_path="${files[real_idx]}"
local old_name="${names[real_idx]}"
# --- ГЛАВНАЯ ЛОГИКА ИЗВЛЕЧЕНИЯ ЦИФРЫ ---
local number=""
if [[ "$old_name" == "${prefix_old}.pdf" ]]; then
number=""
elif [[ "$old_name" =~ ^${prefix_old}([0-9]+)\.pdf$ ]]; then
number="${BASH_REMATCH[1]}"
else
warn "Не удалось распознать номер в файле: $old_name. Пропущено."
((skip_count++))
continue
fi
# -----------------------------------------
# Формируем новое имя
local new_name
if [[ -z "$number" ]]; then
new_name="${prefix_new}.pdf"
else
new_name="${prefix_new}${number}.pdf"
fi
local new_full_path="$dir/$new_name"
# Проверка: существует ли уже целевой файл?
if [[ -f "$new_full_path" ]]; then
echo "⚠️ ПРОПУЩЕНО: $new_name уже существует в папке. Не перезаписываем."
((skip_count++))
continue
fi
# Выполняем переименование
if mv "$old_full_path" "$new_full_path"; then
log "OK: $old_name → $new_name"
((success_count++))
else
error "Ошибка при переименовании: $old_name"
((skip_count++))
fi
done
echo ""
echo "✅ Успешно переименовано: $success_count"
if (( skip_count > 0 )); then
echo "⚠️ Пропущено (файл уже был или ошибка): $skip_count"
fi
echo ""
read -p "Нажмите Enter, чтобы вернуться..." _
;;
3)
# ОЧИСТКА ПАПКИ EPSON: удалить все файлы БЕЗ подтверждения
local file_count=0
local deleted_files=()
# Считаем файлы и собираем их список
while IFS= read -r -d '' file; do
((file_count++))
deleted_files+=("$file")
done < <(find "$dir" -maxdepth 1 -type f -print0)
if [[ $file_count -eq 0 ]]; then
echo ""
log "В папке Epson нет файлов для удаления."
read -p "Нажмите Enter, чтобы вернуться в меню..." _
continue
fi
clear
echo ""
echo "=========================================="
echo " ОЧИСТКА ПАПКИ EPSON"
echo "=========================================="
echo "Папка: $dir"
echo "Найдено файлов: $file_count"
echo ""
echo "Начинаю удаление..."
echo "------------------------------------------"
# Удаляем и сразу запоминаем имена для отчёта
for f in "${deleted_files[@]}"; do
if rm -f "$f"; then
log "Удален: $(basename "$f")"
else
error "Не удалось удалить: $(basename "$f")"
fi
done
echo ""
echo "✅ Все файлы из папки Epson удалены."
echo ""
read -p "Нажмите Enter, чтобы вернуться в главное меню..." _
;;
4)
echo "Возврат в главное меню."
return
;;
*)
echo "Неверный выбор. Введите 1, 2, 3 или 4."
read -p "Нажмите Enter..." _
;;
esac
done
}
delete_numbered_folders() {
# Ищем папки с именами, состоящими только из цифр (1, 2, 3... 10 и т.д.)
local folders_to_delete=()
for item in "$BASE_DIR"/*; do
if [[ -d "$item" ]] && [[ "$(basename "$item")" =~ ^[0-9]+$ ]]; then
folders_to_delete+=("$item")
fi
done
if [[ ${#folders_to_delete[@]} -eq 0 ]]; then
echo ""
warn "В папке $BASE_DIR нет папок с цифровыми именами (1, 2, 3...)."
read -p "Нажмите Enter, чтобы вернуться в меню..." _
return
fi
echo ""
echo "⚠️ НАЙДЕНЫ СЛЕДУЮЩИЕ ПАПКИ ДЛЯ УДАЛЕНИЯ:"
for f in "${folders_to_delete[@]}"; do
echo " - $(basename "$f")"
done
echo ""
read -p "ПАПКИ БУДУТ УДАЛЕНЫ. Нажмите Enter для подтверждения (Ctrl+C для отмены): " confirm
# Если пользователь просто нажал Enter — удаляем. Если Ctrl+C — скрипт прервётся сам.
for f in "${folders_to_delete[@]}"; do
rm -rf "$f"
log "Удалена папка: $(basename "$f")"
done
echo ""
echo "✅ Папки успешно удалены!"
read -p "Нажмите Enter, чтобы вернуться в главное меню..." _
}
copy_special_symbols() {
# Цвета для оформления
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Общий массив символов с разбивкой по категориям
declare -a symbols
declare -a categories
categories[0]="Надстрочные цифры (суперскрипты)"
symbols[0]="¹"; symbols[1]="²"; symbols[2]="³"; symbols[3]="⁴"; symbols[4]="⁵"
symbols[5]="⁶"; symbols[6]="⁷"; symbols[7]="⁸"; symbols[8]="⁹"; symbols[9]="⁰"
categories[1]="Математические символы"
symbols[10]="∑"; symbols[11]="∏"; symbols[12]="∫"; symbols[13]="∂"; symbols[14]="∇"
symbols[15]="∆"; symbols[16]="∞"; symbols[17]="≈"; symbols[18]="≠"; symbols[19]="≤"
symbols[20]="≥"; symbols[21]="∈"; symbols[22]="∉"; symbols[23]="⊂"; symbols[24]="⊃"
symbols[25]="∩"; symbols[26]="∪"; symbols[27]="√"; symbols[28]="∛"; symbols[29]="∜"
categories[2]="Греческие буквы"
symbols[30]="α"; symbols[31]="β"; symbols[32]="γ"; symbols[33]="δ"; symbols[34]="ε"
symbols[35]="ζ"; symbols[36]="η"; symbols[37]="θ"; symbols[38]="ι"; symbols[39]="κ"
symbols[40]="λ"; symbols[41]="μ"; symbols[42]="ν"; symbols[43]="ξ"; symbols[44]="ο"
symbols[45]="π"; symbols[46]="ρ"; symbols[47]="σ"; symbols[48]="τ"; symbols[49]="υ"
symbols[50]="φ"; symbols[51]="χ"; symbols[52]="ψ"; symbols[53]="ω"
categories[3]="Стрелки"
symbols[54]="←"; symbols[55]="→"; symbols[56]="↑"; symbols[57]="↓"; symbols[58]="↔"
symbols[59]="↕"; symbols[60]="⇐"; symbols[61]="⇒"; symbols[62]="⇑"; symbols[63]="⇓"
symbols[64]="⇔"; symbols[65]="⇕"
categories[4]="Знаки препинания и спецсимволы"
symbols[66]="©"; symbols[67]="®"; symbols[68]="™"; symbols[69]="§"; symbols[70]="¶"
symbols[71]="†"; symbols[72]="‡"; symbols[73]="•"; symbols[74]="·"; symbols[75]="…"
# Функция копирования в буфер обмена
copy_to_clipboard() {
local symbol="$1"
if command -v xclip &> /dev/null; then
echo -n "$symbol" | xclip -selection clipboard
echo -e "${GREEN}Скопировано: $symbol${NC}"
elif command -v xsel &> /dev/null; then
echo -n "$symbol" | xsel --clipboard --input
echo -e "${GREEN}Скопировано: $symbol${NC}"
else
echo -e "${RED}Нет утилиты для копирования. Скопируйте символ вручную: ${NC}$symbol"
fi
}
# Вывод списка с разбивкой по категориям
show_symbols() {
local idx=0
local cat_idx=0
for category in "${categories[@]}"; do
echo -e "${BLUE}${category}:${NC}"
# Количество символов в каждой категории
case $cat_idx in
0) count=10;; # Надстрочные цифры
1) count=20;; # Математические символы
2) count=24;; # Греческие буквы
3) count=12;; # Стрелки
4) count=10;; # Знаки препинания
esac
for ((i=0; i<count; i++)); do
printf " %2d) %s\n" "$((idx + 1))" "${symbols[$idx]}"
((idx++))
done
echo ""
((cat_idx++))
done
}
while true; do
clear
echo ""
echo -e "${YELLOW}=== ВЫБОР СПЕЦСИМВОЛОВ (расширенный список) ===${NC}"
echo ""
show_symbols
echo -e "${YELLOW}Как использовать:${NC}"
echo "1. Найдите нужный символ в списке."
echo "2. Введите его номер для копирования."
echo "3. Для выхода нажмите Enter или введите 'q'."
echo ""
read -r -p "> " choice
# Выход по нажатию Enter
if [ -z "$choice" ]; then
break
fi
case $choice in
q|Q)
break
;;
[1-9]|[1-7][0-5]) # Номера от 1 до 75
if [ "$choice" -ge 1 ] && [ "$choice" -le 75 ]; then
selected_symbol="${symbols[$((choice-1))]}"
copy_to_clipboard "$selected_symbol"
else
echo -e "${RED}Неверный номер. Попробуйте снова.${NC}"
fi
;;
*)
echo -e "${RED}Неверный ввод. Введите номер символа, 'q' или нажмите Enter для выхода.${NC}"
;;
esac
echo ""
read -p "Нажмите Enter, чтобы выбрать другой символ или выйти..." _
done
}
main() {
check_gs
while true; do
show_main_menu
read -rp "Выберите пункт: " choice
case "$choice" in
1) create_base_and_folders ;;
2) create_epson_folder ;;
3) create_compress_folder ;;
4) submenu_pdf_compress ;;
5) merge_images_to_pdf ;;
6) extract_archives ;;
7) rename_accompanying_file ;;
8) delete_numbered_folders ;;
9) copy_special_symbols ;;
0)
echo "Выход из программы."
exit 0
;;
*)
warn "Неверный пункт. Попробуйте снова."
sleep 1
;;
esac
done
}
# Запуск
main
---------------------------------------------------------------------------------------------------------------------------------------------
Воспользуйтесь терминалом вызовите его сочетанием клавиш Ctrl+Alt+T и просто перекиньте этот фаил в терминал из окна рабочего стола или проводника смотря где вы его создавали.
Примечание !!!!!!!!
Фаил может не сработать если не стоит в свойствах файла галочка Является выполняемым и не стоят права на просмотр и изменение.
Скопируйте этот код с этой страницы и сохраните его как menu.sh