Разбирались для задачи
Задача 3: Создайте реальный скрипт
text
Используя heredoc, создайте скрипт /tmp/cleanup.sh который:
1. Удаляет временные файлы старше 7 дней из /tmp
2. Очищает логи apache старше 30 дней
3. Проверяет свободное место на диске
4. Логирует свои действия в /var/log/cleanup.log
Решение тут
Массив какие бывают
Как посмотреть что внутри массива сразу все
echo ${my_array[@]} # Все элементы массива
И по очереди (это скрипт)
for item in "${my_array[@]}";do
echo "$item"
done
На выходе мы получим если масив состоял из my_array=("apple" "banana" "cherry") или
declare -A user_shells
user_shells=( ["root"]="/bin/bash" ["guest"]="/bin/sh" )
то на выходе
в первом случае
будет так
apple
banana
cherry
во втором случае с объявлением
будет вывод
/bin/bash
/bin/sh
как во втором случае добавить в масив вот так
user_shells["admin"]="/usr/bin/zsh"
Как узнать размер массива
echo ${#my_array[@]}
вот этот символ # это он
Как пропарить массив 2 варианта и чтобы вывелось на экран или в команду
# Явное объявление (необязательно, но желательно)
declare -a my_array
# Присваивание значений
my_array=("apple" "banana" "cherry")
# Добавление элемента
my_array+=("date")
- Сделали скрипт
#!/bin/bash
my_file=("file1.sh" "file2.sh" "sc.sh")
for file in "${!my_file[@]}"; do
echo "$file"
echo ${my_file[$file]}
echo $file
#echo "Сейчас был в цикле элемент массива "
done
5555
bash
echo ${my_array[0]} # Первый элемент (apple)
echo ${my_array[@]} # Все элементы массива
echo ${#my_array[@]} # Количество элементов в массиве
echo ${!my_array[@]} # Все индексы (0 1 2 3)
цвроыл
Вырезать часть массива (пропустить 1, взять 2):
bash
echo ${fruits[@]:1:2} # banana cherry
Решение первого пункта задачи
#!/bin/bash
mapfile -t my_array < <(find . -type f -ctime -2 ! -name "clear.sh" -printf "%f\n")
for item in "${my_array[@]}";do
echo "delete $item"
rm "$item"
echo "Udalili ++"
done
вот такой же но доработанный скрипт
#!/bin/bash
# 1. Оставляем полные пути (убираем -printf %f)
# 2. Используем -print0 для защиты от пробелов в именах
mapfile -d $'\0' -t my_array < <(find . -type f -ctime -2 ! -name "clear.sh" -print0)
# Проверка, есть ли что удалять
if [ ${#my_array[@]} -eq 0 ]; then
echo "Свежих файлов для удаления не найдено."
exit 0
fi
for item in "${my_array[@]}"; do
# Проверяем, существует ли файл (на всякий случай)
if [ -f "$item" ]; then
echo "Удаляю: $item"
rm "$item"
fi
done
echo "Готово. Удалено файлов: ${#my_array[@]}"
Вопрос
что делают дополнительные атрибуты, почему нельзя просто -t а тут стоит mapfile -d $’\0′ и вот второй вопрос что делает -print0 именно 0
Ответ
1. Почему нельзя просто mapfile -t, а нужно -d $'\0'?
По умолчанию mapfile -t считает, что один элемент массива заканчивается там, где стоит перенос строки (\n).
В чем подвох:
В Linux (хоть это и плохая практика) имя файла может содержать перенос строки. Если у вас есть файл с именем "My\nFile.txt", то mapfile -t создаст два элемента массива: My и File.txt. Скрипт попытается удалить их по отдельности и выдаст ошибку, либо (что хуже) удалит не то.
-d— это флаг разделителя (delimiter).$'\0'— это нулевой байт (null terminator).
В Linux нулевой байт — это единственный символ, который физически запрещен в именах файлов. Поэтому это единственный на 100% надежный разделитель. Мы говорим массиву: «Считай, что файл закончился только тогда, когда встретишь \0, а не обычный Enter».
2. Что делает -print0 (и именно цифра 0)?
По умолчанию find после каждого найденного имени печатает невидимый символ \n (перенос строки).
-print0заставляетfindвместо переноса строки лепить в конце каждого имени тот самый нулевой байт (\0).
Как это работает в связке:
find -print0выплевывает:file1.txt\0file2.txt\0file с пробелом.txt\0mapfile -d $'\0'ловит этот поток и аккуратно разрезает его строго по этим\0.
Почему именно 0?
Это стандарт в программировании (C, Bash, Python). Нулевой байт (ASCII 0) используется как «стоп-сигнал». Он не является буквой, цифрой или пробелом. Его невозможно напечатать на клавиатуре, и его не может быть внутри имени файла.
Итог:
Если вы на 200% уверены, что в ваших именах файлов никогда не будет пробелов и странных символов, можно использовать mapfile -t.
Но если вы пишете скрипт, который должен работать всегда и везде (best practice), то связка find -print0 + mapfile -d $'\0' — это единственный способ гарантировать, что скрипт не сойдет с ума, встретив файл "отчет за 2024 год.pdf".
Разбор
именно $
Шаг 1: Что такое $ в этом месте?
Здесь $ — это «ключ» или «команда» для баша, которая говорит:
«Эй, bash! То, что в кавычках — это не просто текст, а специальный код!»
Без $ bash думал бы, что '\0' — это просто текст из 3 символов:
- символ
' - символ
\ - символ
0 - символ
'
Шаг 2: Что делает $'\0'?
$'\0' превращает эти 4 символа в один специальный символ — НУЛЕВОЙ символ.
Представьте это так:
'\0'= просто текст (как написать на бумаге слово «ноль»)$'\0'= реальный нулевой символ (как нажать кнопку «ноль» на калькуляторе)
Шаг 3: Как выглядит нулевой символ?
В компьютере нулевой символ:
- Это символ с кодом 0
- Он невидимый (как пробел, но еще более «ничего»)
- Он используется как разделитель между именами файлов
Шаг 4: Зачем он нужен в -d $'\0'?
Команда mapfile читает список файлов и должна знать: «где заканчивается одно имя файла и начинается другое?»
Обычно используют:
- Пробел — но если в имени файла есть пробел, все сломается
- Перевод строки — но если в имени файла есть перевод строки (редко, но бывает), тоже сломается
Нулевой символ \0 — это идеальный разделитель, потому что:
- Он НИКОГДА не может быть в имени файла (в Linux запрещено)
- Он абсолютно надежен
Шаг 5: Аналог из реальной жизни
Представьте, что у вас есть список продуктов для покупки:
Обычный способ (с пробелами):
text
молоко яйца хлеб с маслом сыр
Проблема: «хлеб с маслом» — это один товар или два? Непонятно!
Способ с нулевыми символами:
text
молоко\0яйца\0хлеб с маслом\0сыр\0
Компьютер точно знает: после каждого \0 — новый товар!
Шаг 6: Что делает вся строка?
bash
mapfile -d $'\0' -t my_array < <(find ... -print0)
Разберем по кусочкам:
find ... -print0— командаfindищет файлы и выводит их имена, разделяя нулевыми символами (\0)
Пример вывода:./file1\0./doc file.txt\0./photo.jpg\0-d $'\0'— говоритmapfile:
«Читай данные, разделяя их по нулевому символу (\0), а не по переводу строки»-t— «обрезай переводы строк в конце каждого имени файла»my_array— складывай все имена файлов в массив с этим именем
Шаг 7: Что было бы БЕЗ $'\0'?
Без $ bash не поймет, что '\0' — это нулевой символ:
bash
# Так НЕ РАБОТАЕТ: mapfile -d '\0' -t my_array < <(find ... -print0)
mapfile будет искать разделителем текст \0 (символ \ и символ 0), а не нулевой символ. Команда find выводит нулевые символы, а mapfile ищет текст \0 — они не совпадают!
Шаг 8: Простой тест для понимания
Создайте файл и попробуйте:
bash
# Создаем файл с нулевыми символами
echo -e "первый\0второй с пробелом\0третий" > test.txt
# Смотрим содержимое (нули не видны)
cat test.txt
# Вывод: первыйвторой с пробеломтретий (все слитно!)
# Теперь читаем правильно:
mapfile -d $'\0' -t arr < test.txt
echo "Элементов: ${#arr[@]}"
# Вывод: Элементов: 3 (ура! три отдельных элемента)
📌 Итог простыми словами:
$ в $'\0' — это «переводчик» или «преобразователь»:
- Без
$:'\0'= просто текст «обратный слэш-ноль» - С
$:$'\0'= реальный нулевой символ (ASCII код 0)
Это нужно, чтобы:
find(с флагом-print0) выводил имена файлов, разделяя их нулямиmapfile(с флагом-d $'\0') понимал, что разделитель — нули, а не переводы строк- Скрипт корректно работал с любыми именами файлов, даже с пробелами или спецсимволами
Короче: $ превращает текст '\0' в настоящий нулевой символ, который безопасно разделяет