Vai al contenuto
LF Lomazzi Federico

Home/Software/BASH

BASH

Backup incrementale con rotazione (bash)

Un solo file, nessuna dipendenza.

Lomazzi Federico MIT bkinc-6c91e.sh · 5 KB 188 righe aggiornato il 23 agosto 2026

Vi serve un bash veloce per un backup incrementale rapido, sicuro e senza troppe pretese? Questo bash l'ho usato varie volte a chiusura lavoro per il backup di folder sulle quali ci ritornavo sporadicamente, ma dove era necessario sapere sempre dove ci si era fermati. Un bash senza fronzoli ma che fà il suo lavoro.

Snapshot giornalieri con hard link: occupano lo spazio di uno e si sfogliano come fossero trenta....

Il codice

bkinc-6c91e.sh · BASH · 188 righe · MIT

#!/usr/bin/env bash
#
# Lomazzi Federico 8/12/2023
# Backup incrementale con rotazione
# Sincronizzato; quello che non cambia non viene copiato x volte..

set -u

# ----------------------------------------------------------------------
# Configurazione
# ----------------------------------------------------------------------

SOURCE="/home/federico/dati"
BACKUP_ROOT="/backup/snapshots"

KEEP_DAYS=30

DATE="$(date +%Y-%m-%d)"
DEST="$BACKUP_ROOT/$DATE"
TMP="$BACKUP_ROOT/.snapshot-$DATE.tmp"

# ----------------------------------------------------------------------
# Controlli
# ----------------------------------------------------------------------

if [ ! -d "$SOURCE" ]; then
    echo "ERRORE: directory sorgente inesistente: $SOURCE"
    exit 1
fi

mkdir -p "$BACKUP_ROOT" || exit 1

if [ -e "$DEST" ]; then
    echo "Lo snapshot di oggi esiste già: $DEST"
    exit 0
fi

# Evito di lasciare in giro un eventuale backup interrotto.
rm -rf "$TMP"
mkdir -p "$TMP" || exit 1

# ----------------------------------------------------------------------
# Cerco l'ultimo snapshot
# ----------------------------------------------------------------------

PREVIOUS=""

while IFS= read -r dir; do
    [ "$dir" = "$TMP" ] && continue
    [ "$dir" = "$DEST" ] && continue

    PREVIOUS="$dir"
done < <(
    find "$BACKUP_ROOT" \
        -mindepth 1 \
        -maxdepth 1 \
        -type d \
        -name '????-??-??' \
        -print 2>/dev/null | sort
)

# ----------------------------------------------------------------------
# BASE DEL NUOVO SNAPSHOT
# ----------------------------------------------------------------------

if [ -n "$PREVIOUS" ] && [ -d "$PREVIOUS" ]; then

    echo "Snapshot precedente: $PREVIOUS"

    # Questa è la parte interessante:
    # copio la struttura ma i file sono hard link, quindi spazio quasi zero.
    cp -al "$PREVIOUS"/. "$TMP"/ || {
        echo "ERRORE durante la clonazione dello snapshot precedente."
        rm -rf "$TMP"
        exit 1
    }

else

    echo "Primo backup: nessuno snapshot precedente."

fi

# ----------------------------------------------------------------------
# Sincronizzo
# ----------------------------------------------------------------------

echo "Analisi dei file..."

while IFS= read -r -d '' SRC; do

    REL="${SRC#"$SOURCE"/}"
    DST="$TMP/$REL"

    if [ -d "$SRC" ]; then

        mkdir -p "$DST"

    elif [ -L "$SRC" ]; then

        LINK_SOURCE="$(readlink "$SRC")"
        LINK_DEST=""

        if [ -L "$DST" ]; then
            LINK_DEST="$(readlink "$DST")"
        fi

        if [ "$LINK_SOURCE" != "$LINK_DEST" ]; then
            rm -rf "$DST"
            ln -s "$LINK_SOURCE" "$DST"
        fi

    elif [ -f "$SRC" ]; then

        # Se il file è identico non faccio nulla (resta collegato fisicamente allo snapshot precedente)..
        if [ -f "$DST" ] && cmp -s "$SRC" "$DST"; then
            continue
        fi
        # ...se invece è cambiato rompo l'hard link e copio il nuovo file.
        rm -f "$DST"
        mkdir -p "$(dirname "$DST")" # creo la folder dst
        cp -p "$SRC" "$DST" || {
            echo "ERRORE copiando: $SRC"
            rm -rf "$TMP"
            exit 1
        }

    fi

done < <(find "$SOURCE" -mindepth 1 -print0)

# ----------------------------------------------------------------------
# Elimino dallo snapshot quello che non c'è più
# ----------------------------------------------------------------------

while IFS= read -r -d '' DSTFILE; do

    REL="${DSTFILE#"$TMP"/}"
    SRCFILE="$SOURCE/$REL"

    if [ ! -e "$SRCFILE" ] && [ ! -L "$SRCFILE" ]; then
        rm -rf "$DSTFILE"
    fi

done < <(find "$TMP" -mindepth 1 -depth -print0)

# Copio anche permessi e date delle directory.
while IFS= read -r -d '' SRCDIR; do

    REL="${SRCDIR#"$SOURCE"/}"
    DSTDIR="$TMP/$REL"

    [ -d "$DSTDIR" ] && chmod --reference="$SRCDIR" "$DSTDIR" 2>/dev/null
    [ -d "$DSTDIR" ] && touch -r "$SRCDIR" "$DSTDIR" 2>/dev/null

done < <(find "$SOURCE" -mindepth 1 -type d -print0)

# ----------------------------------------------------------------------
# completato
# ----------------------------------------------------------------------
mv "$TMP" "$DEST" || {
    echo "!ERRORE durante la chiusura dello snapshot."
    exit 1
}
echo "Creato: $DEST"

# ----------------------------------------------------------------------
# Qui gestisco la rotazione
# ----------------------------------------------------------------------

COUNT=0
while IFS= read -r SNAPSHOT; do
    COUNT=$((COUNT + 1))
    if [ "$COUNT" -gt "$KEEP_DAYS" ]; then
        echo "Elimino vecchio snapshot: $SNAPSHOT"
        rm -rf "$SNAPSHOT"
    fi
done < <(
    find "$BACKUP_ROOT" \
        -mindepth 1 \
        -maxdepth 1 \
        -type d \
        -name '????-??-??' \
        -print 2>/dev/null |
    sort -r
)
echo "Backup terminato."
exit 0
24 visite

Commenti

Nessun commento, per ora.

Continua

Altri strumentidello stesso genere

Tutto il codice

BASH MIT

Syskiller

Report FULL SYSTEM single bash

Vi raccoglie in un unico report load, cpu, ram, swap, dischi, inode, processi pesanti, porte in ascolto, connessioni, servizi falliti, errori kernel, OOM killer, ultimi login, file grossi e directory che vi stanno esplodendo. Utile per poi spararlo via alla AI preferita che in tempo zero fà in 1 minuto quello che facevamo noi prima in 2 ore... :-)) ** Sempre prestando occhio anche a cosa fà la AI, ovviamente **

18 visite

BASH MIT

Monitor integrità directory

Monitor files nuovi/eliminati/modificati

Vi è mai capitato di dovervi scansionare un sacco di directory e files per poi accorgervi che ad essere broken era un symlink o maree di inodes? Questo potrebbe essere lo strumento che fà per voi. Facile da usare, veloce e sopratutto và diritto al punto.

16 visite

JAVASCRIPT MIT

Questo gioco è tornato otto anni dopo, e non l'ho più scritto io.

Vecchi giochi, nuove competenze

Nel 2018 avevo scritto per mio figlio un piccolo gioco educativo in JavaScript usando Phaser, impiegandoci tre o quattro serate. Otto anni dopo lui lo ha ripreso ed in una frazione di quel tempo ne ha costruito una versione enormemente più ricca usando l'AI come esecutore tecnico. La parte interessante non è che l'AI sappia programmare, ma che il collo di bottiglia si sia spostato dal codice alla capacità di progettare, verificare e correggere.

26 visite

Scarica