Загрузка данных
#!/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