[stackoverflow](https://stackoverflow.com/questions/32077318/generating-a-bash-script-from-a-bash-script)
- Скрипт запускается в корне нужного дерева, проходит по всем каталогам и генерирует другой bash‑скрипт
recreate_tree.sh. [stackoverflow](https://stackoverflow.com/questions/32077318/generating-a-bash-script-from-a-bash-script) - В
recreate_tree.shесть переменнаяTARGET_BASE— при запуске её можно указать аргументом (корень, куда восстанавливать структуру), по умолчанию берётся текущий каталог. [stackoverflow](https://stackoverflow.com/questions/32077318/generating-a-bash-script-from-a-bash-script) - Для каждого каталога исходного дерева генерируются команды:
mkdir -p— создаёт каталог (и родителей, если их ещё нет);chmod— восстанавливает режим доступа (права);chown(если скрипт запущен от root) — восстанавливает владельца и группу по UID/GID;touch -aиtouch -m— восстанавливают время доступа и модификации. [linuxize](https://linuxize.com/cheatsheet/tar/tar-cheatsheet.pdf)- Дополнительно, если в системе есть
getfacl/setfacl, скрипт сохраняет и восстанавливает ACL; если естьgetfattr/setfattr, — расширенные атрибуты (xattr). [shellmap.eversources](https://shellmap.eversources.app/task/view-acl-extended-permissions) - Сам сгенерированный
recreate_tree.shделается исполняемым с правами755, чтобы его можно было сразу запускать. [itslinuxfoss](https://itslinuxfoss.com/make-bash-script-executable-using-chmod/)
Итого: первый скрипт — “снимок структуры и метаданных в виде bash”, второй (recreate_tree.sh) — “реплейер”, который на любой базе (TARGET_BASE) создаёт такое же дерево каталогов с максимально полным набором метаинформации. [gnu](https://www.gnu.org/software/tar/manual/html_section/Attributes.html)
######################################################
#!/usr/bin/env bash
set -euo pipefail
out="recreate_tree.sh"
src="$(pwd)"
{
cat <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
TARGET_BASE="${1:-$(pwd)}"
have_chown=0
[[ $EUID -eq 0 ]] && have_chown=1
EOF
Каталоги
find "$src" -depth -type d | while IFS= read -r dir; do
rel="${dir#$src}"
if [[ -z "$rel" ]]; then
t='${TARGET_BASE}'
else
printf -v qrel '%q' "$rel"
t="\${TARGET_BASE}${qrel}"
fi
mode="$(stat -c '%a' "$dir")"
uid="$(stat -c '%u' "$dir")"
gid="$(stat -c '%g' "$dir")"
atime="$(stat -c '%x' "$dir" | sed 's/\.[0-9]\+ .*//')"
mtime="$(stat -c '%y' "$dir" | sed 's/\.[0-9]\+ .*//')"
echo "mkdir -p -- $t"
echo "chmod $mode -- $t"
echo "if [[ \$have_chown -eq 1 ]]; then chown $uid:$gid -- $t; fi"
printf 'touch -a -d %q -- %s\n' "$atime" "$t"
printf 'touch -m -d %q -- %s\n' "$mtime" "$t"
echo
done
Симлинки
find "$src" -type l | while IFS= read -r link; do
rel="${link#$src}"
if [[ -z "$rel" ]]; then
t='${TARGET_BASE}'
else
printf -v qrel '%q' "$rel"
t="\${TARGET_BASE}${qrel}"
fi
link_target="$(readlink "$link")"
printf 'ln -sfn -- %q %s\n' "$link_target" "$t"
echo
done
} > "$out"
chmod 755 "$out"
echo "Создан: $out"
################# вариант задротов ##############
#!/usr/bin/env bash
set -euo pipefail
out="recreate_tree.sh"
src="$(pwd)"
tmp_archive="$(mktemp)"
trap 'rm -f "$tmp_archive"' EXIT
(
cd "$src"
find . -depth -print0 | cpio --null -o -H newc > "$tmp_archive"
)
archive_size="$(wc -c < "$tmp_archive" | tr -d ' ')"
cat > "$out" <<EOF
#!/usr/bin/env bash
set -euo pipefail
TARGET_BASE="\${1:-\$(pwd)}"
SELF="\$0"
ARCHIVE_OFFSET=\$((LINENO + 3))
mkdir -p -- "\$TARGET_BASE"
tail -c +$((archive_size + 1)) /dev/null >/dev/null 2>&1 || true
archive_line=\$(awk '/^__CPIO_ARCHIVE_BELOW__$/ { print NR + 1; exit }' "\$SELF")
if [[ -z "\$archive_line" ]]; then
echo "Ошибка: встроенный архив не найден" >&2
exit 1
fi
tail -n +"\\\$archive_line" "\$SELF" | (
cd "\$TARGET_BASE"
cpio -idm --quiet
)
exit 0
__CPIO_ARCHIVE_BELOW__
EOF
cat "$tmp_archive" >> "$out"
chmod 755 "$out"
echo "Создан: $out"
[file-name 000377_2026-06-20_21-38-58.txt]