Загрузка данных


#!/bin/bash

MyName="Ivanov"
ScriptName=$(basename "$0")

phrase=""
output_file=""
count_mode=0
target_dir=""
search_chars_file=""

# По умолчанию ищем звездочку или тире
search_patterns=('\*' '-')

show_help() {
    echo "Использование: $0 [ключи] [файл1 файл2 ...]" >&1
    echo "Ключи:" >&1
    echo "  -h           Вывод этой справки" >&1
    echo "  -p 'фраза'   Задать фразу для замены через аргумент" >&1
    echo "  -P           Запросить фразу для замены у пользователя через ввод" >&1
    echo "  -o 'файл'    Перенаправить вывод результата в указанный файл" >&1
    echo "  -c           Подсчитать общее количество произведенных замен" >&1
    echo "  -d 'каталог' Обработать все файлы в указанном каталоге" >&1
    echo "  -f 'файл'    Прочитать символы для поиска из указанного файла" >&1
}

if [ $# -eq 0 ]; then
    echo "Ошибка: Не указаны аргументы." >&2
    show_help
    exit 1
fi

# Обработка ключей через getopts
# Двоеточие после буквы означает, что у ключа есть обязательный аргумент (например, p:, o:)
while getopts "hp:Po:cd:f:" opt; do
    case [ "$opt" ] in
        h)
            show_help
            exit 0
            ;;
        p)
            phrase="$OPTARG"
            ;;
        P)
            echo -n "Введите фразу для замены: " >&1
            read -r phrase
            ;;
        o)
            output_file="$OPTARG"
            ;;
        c)
            count_mode=1
            ;;
        d)
            target_dir="$OPTARG"
            ;;
        f)
            search_chars_file="$OPTARG"
            ;;
        *)
            show_help
            exit 1
            ;;
    esac
done

# Сдвигаем указатель параметров, чтобы убрать обработанные ключи
shift $((OPTIND - 1))

# Если фраза не задана, используем имя скрипта без пути
if [ -z "$phrase" ]; then
    phrase="$ScriptName"
fi

# Если включен режим вывода в файл, перенаправляем стандартный вывод (дескриптор 1)
if [ -n "$output_file" ]; then
    exec > "$output_file"
fi

# Накапливаем список файлов для обработки
files_to_process=("$@")

# Если задан ключ -d (Повышенная сложность)
if [ -n "$target_dir" ]; then
    if [ ! -d "$target_dir" ] || [ ! -r "$target_dir" ]; then
        echo "Ошибка: Каталог '$target_dir' не существует или недоступен для чтения." >&2
        exit 2
    fi
    # Читаем только файлы из этого каталога (не папки)
    while IFS= read -r -d '' f; do
        files_to_process+=("$f")
    done < <(find "$target_dir" -maxdepth 1 -type f -print0 2>/dev/null)
fi

# Если файлов для обработки нет вообще
if [ ${#files_to_process[@]} -eq 0 ]; then
    echo "Ошибка: Отсутствуют файлы для обработки." >&2
    exit 3
fi

# Если задан ключ -f (Повышенная сложность)
if [ -n "$search_chars_file" ]; then
    if [ ! -r "$search_chars_file" ]; then
        echo "Ошибка: Файл с символами поиска '$search_chars_file' не существует или не читается." >&2
        exit 2
    fi
    # Перезаписываем массив символов поиска данными из файла
    search_patterns=()
    while read -r -a words; do
        for word in "${words[@]}"; do
            # Экранируем спецсимволы для корректной работы регулярных выражений awk
            escaped=$(echo "$word" | sed 's/[*?+^$()|{}[\]\\]/\\&/g')
            search_patterns+=("$escaped")
        done
    done < "$search_chars_file"
fi

# Валидация всех найденных файлов на читаемость
for file in "${files_to_process[@]}"; do
    if [ ! -r "$file" ]; then
        echo "Ошибка: Текстовый файл '$file' не существует или не читается." >&2
        exit 2
    fi
done

# Формируем регулярное выражение для AWK из наших символов поиска
# Пример: если ищем '*' и '-', получится (\*|-)
awk_regex=""
for p in "${search_patterns[@]}"; do
    if [ -z "$awk_regex" ]; then
        awk_regex="$p"
    else
        awk_regex="$awk_regex|$p"
    fi
done

current_time=$(date "+%Y-%m-%d %H:%M:%S")
echo "=== RUNNING SCRIPT at $current_time by $MyName for TASK #12 ==="

total_replacements=0

# Основной цикл обработки файлов
for file in "${files_to_process[@]}"; do
    
    # Запускаем AWK для обработки файла. 
    # Он ищет совпадения, делает замену, считает их количество и выводит измененный текст.
    awk_result=$(awk -v reg="$awk_regex" -v repl="$phrase" '
    BEGIN { reps = 0; }
    {
        # Встроенная функция gsub выполняет глобальную замену по регулярному выражению
        # и возвращает количество успешных замен в строке
        count = gsub(reg, repl);
        reps += count;
        print $0;
    }
    END { print "===INF===" reps; }
    ' "$file")

    # Вытаскиваем количество замен, выполненных AWK в этом конкретном файле
    file_reps=$(echo "$awk_result" | tail -n 1 | sed 's/===INF===//')
    
    # Сохраняем измененный текст (без технической строки ===INF===) обратно в файл
    echo "$awk_result" | sed '$d' > "$file.tmp" && mv "$file.tmp" "$file"

    # Если замены были произведены, выводим путь к файлу ровно ОДИН раз
    if [ "$file_reps" -gt 0 ]; then
        echo "Pattern replaced in $file"
        total_replacements=$((total_replacements + file_reps))
    fi
done

# Если включен режим подсчета (-c), выводим общую сумму замен по всем файлам
if [ $count_mode -eq 1 ]; then
    echo "Total replacements made: $total_replacements"
fi

exit 0