4backup : Shell Script
AVISO: Se ha modificado el script. Anteriormente a menos que tuviésemos un servidor de correos configurado en local o haciendo relay a una cuenta por ejemplo de gmail, era imposible ver si el programa fallaba por algún motivo (directorios inaccesibles, unidades externas no montadas, etc…). Ahora comprueba si el directorio $BASE permite la escritura y crea la jerarquía de directorios. En caso de fallo podremos acudir al log.
Este script pertenece a la segunda parte de la entrada Backups con tar: fullbackups, incrementales y diferenciales. Existe una tercera y última parte Usuario para crear respaldos con 4backup en la que crearemos un usuario restringido propietario y creador de los backups.
El script está bastante comentado ya que se creó como ejercicio de práctica del Capítulo -9 El entorno de Consola, Shell Scripts, el Correo Electrónico y uso básico de SQL en el que además aprenderemos a configurar el servidor de correos postfix para que haga relay a gmail y así recibir los email.
#!/bin/bash ################################################################################## # | || | | |__ __ _ ___| | ___ _ _ _ # # | || |_| '_ \ / _` |/ __| |/ / | | | '_ \ # # |__ _| |_) | (_| | (__| <| |_| | |_) | # # |_| |_.__/ \__,_|\___|_|\_\\__,_| .__/ # # |_| # ################################################################################## # 4backup crea copias de seguridad en función de los días pasado como parámetros. # El primer parámetro determina que día se realiza fullbackup y el segundo y último # parámetro indica el día para la copia diferencial. # Para el resto de los días de la semana se crearán backups incrementales. # Después de realizarse los fullbackups, estos serán comprobados y si todo es OK # todas las copias anteriores a esos fullbackup serán eliminadas. Puede adaptarse # a necesidades, como por ejemplo en vez de eliminar las copias moverlas a otro disco. ################################################################################## # # # autor: nebul4ck # # fecha: 28-3-2015 # # última modificación: 27-7-2015 # # web: https://nebul4ck.wordpress.com # # # ################################################################################## # Como se va a lanzar desde cron lo primero es ejecutar mediante source el archivo # de configuración del entorno del usuario: ~.profile (Debian) o ~/.bash_profile # (Red Hat) . ~/.profile # // VARIABLES A MODIFICAR POR EL USUARIO // # Directorio BASE del programa 4backup (la raíz de los archivos y dir creados por 4backup). BASE="/home/-USUARIO-/4backup" # ¿ Donde se encuentra montado el disco externo en el que guardaremos las copias ? MOUNTPOINT="/media/USB" # ¿ En que directorio quieres que se guarden las copias de seguridad dentro del disco externo ? DESTINO="$MOUNTPOINT/Mis Backups" # Lista de directorios a respaldar LISTA_DIR=(/home/jjgomez/Imágenes /opt/apps /usr/local/share) # Si tenemos un servidor de correos local configurado o que haga relay a un correo de gmail por # ejemplo, podremos recibir las alertas y el log por mail EMAIL="usuario@hostname" # // VARIABLES GLOBALES PARA 4BACKUP // # Fecha usada para la extensión de los archivos tar EXT="`date +%d%b%y.tar`" # Día de la semana. Útil par cuando el programa comprueba que día de la semana es. DIA=`date | cut -d" " -f1` # Directorio donde serán guardados los snapshot de los backups con tar SNAP="$BASE/4history" # Directorio para archivos de registros (logs) LOGS="$BASE/log" # Archivo de registro LOG="$BASE/log/4backup.log" # Archivos de registros antiguos OLDLOGS="$BASE/log/old" # Archivo de emergencia. Usado cuando no ha sido posible iniciar el programa y ni # enviar el correo al destino. ARCHEMERG="$BASE/emerg.4backup" # FUNCIONES # Mostrar ayuda help4backup() { echo -e "Especificar el día para copias full y el día para copias diferenciales (respectivamente).\nEl resto de días se realizarán copias incrementales\n" echo -e "\tEjemplo: ./4backup lun jue\n" echo -e "Nota: Los días en formato corto y Español" exit 0 } # Función que comprueba si existen los directorios a respaldar compruebaDir() { if [ ! -d "$DIR" ] then echo -e "ERROR: No es posible respaldar $DIR. El directorio no existe.\n" continue fi } # Comprueba los Backups checkBackup() { ESTADO="OK" if [ "$1" == "error" ] then ESTADO="ERROR" echo -e "\nEMERG: No se pudo verificar el archivador $TAR. Se recomienda revisar la última copia\n" elif [ "$1" == "$TAR".gz ] then echo -e "\nINFO: Comprobando backup...\n" if [ "$2" == "incremental" ] then CAMBIOS=`tar -tf "$TAR".gz | wc -l` echo -e "\nSe han realizado $CAMBIOS cambios - $(date "+[%x %X]")\n" fi gzip -vt "$TAR".gz if [ "$?" -ne "0" ] then ESTADO="ERROR" else echo -e "\nLa copia $TAR.gz ha finalizado correctamente - $(date "+[%x %X]")\n" fi fi } # Envía email enviaCorreo() { exec &> /dev/null mail -s "$1" tudirección@gmail.com << EOF Correo de notificación $(date "+[%x %X]") $2 Aviso: Mensaje generado automáticamente. EOF if [ "$?" -ne "0" ] then if [ "$RETORNO" == "error" ] then echo -e "Mail no enviado. Revise /var/log/mail.* o maillog\n\nEl error por el que la aplicación no ha iniciado es:\n$2" > "$ARCHEMERG" else echo -e "Mail no enviado. Revise /var/log/mail.* o maillog\n" >> "$LOG" fi else echo -e "Mail enviado!\n" fi exit 0 } # COMPROBACIONES PREVIAS # Comprobar el número de parámetros y si estos coinciden DIAS=(lun mar mié jue vie sáb dom) if [ "$#" -lt "2" ] || [ "$#" -ge "3" ] then help4backup else # Dos maneras diferentes de desplegar el contenido completo de un array @ y * if ! echo "${DIAS[@]}" | grep -qw "$1" && ! echo "${DIAS[*]}" | grep -qw "$2"; then help4backup; fi fi # Creamos el directorio base y log para registrar posibles errores durante la ejecución if [ ! -d "$LOGS" ] then mkdir -p "$LOGS" &>> /dev/null if [ "$?" -ne "0" ] then echo -e "No es posible escribir en el directorio base ($BASE). \nEl directorio $LOGS no puede ser creado.\nInterrumpiendo ejecución...\n" exit 1 fi fi # Abrimos log exec &> "$LOG" # Comprueba que el dispositivo este montado, mediante mtab y su directorio (punto de montaje) if ! cat /etc/mtab | grep -qw "$MOUNTPOINT"; then RETORNO="error" enviaCorreo "Dispositivo no montado" "El dispositivo ($MOUNTPOINT) no se encuentra montado. Abortado"; fi for dir in "$DESTINO" "$BASE" "$SNAP" "$OLDLOGS" do test -d "$dir" || mkdir -p "$dir" &>> /dev/null if [ "$?" -ne "0" ] then case "$dir" in "$DESTINO") MSG="No se pudo crear el directorio $DESTINO. Abortado.";; "$BASE") MSG="No se pudo crear el directorio $BASE. Abortado.";; "$SNAP") MSG="No se pudo crear el directorio $SNAP. Abortado.";; "$OLDLOGS") MSG="No se pudo crear el directorio $OLDLOGS. Abortado.";; esac enviaCorreo "Error al iniciar 4Backup" "$MSG" fi done # Abrimos log de ejecución exec &> "$LOG" # Inicia programa for ((n=0;n<"${#LISTA_DIR[@]}";n++)) { # Seteamos variables para cada directorio a respaldar: DIR="${LISTA_DIR[$n]}" # Esta es una forma de obtener el último campo de una línea (con egrep) en las copias diferenciales # se muestra otra forma de obtener el último campo de línea con awk NOMBRE=`echo "$DIR" | egrep -o "[^/]+$"` HIST="$SNAP"/"$NOMBRE".snap # Comprobamos si existe copia full de ese directorio: if [ ! -e "$HIST" ] || [ "$1" == "$DIA" ] then echo -e "----------------------------------------------------------------------------------------------" echo -e "La copia para $DIR será full.\n" TIPO="full" else if [ "$DIA" == "$2" ] then TIPO="dif" else TIPO="inc" fi fi case "$TIPO" in full) PREFIJO="full" TAR="$DESTINO"/"$PREFIJO"_"$NOMBRE"_"$EXT" # Función que comprueba si existe el directorio a respaldar. compruebaDir "$DIR" if [ -f "$HIST" ] then # Se elimina el snapshot de tar para que la nueva copia sea full rm "$HIST" echo -e "Archivo histórico $HIST eliminado\n" fi echo -e "Iniciando FullBackup para $DIR\t $(date "+[%x %X]")\n" # Nos movemos al directorio que queremos respaldar. De esta forma evitaremos agregar la ruta completa al archivo tar. cd "$DIR" # Podríamos haber hecho esto de otras maneras pero está es en la que mejor veremos lo que se hace. Primero se crea el tar full # si el tar se ha finalizado correctamente se comprime con grado 8 y se llama a la función checkBackup para comprobar la compresión. if tar -cpvWf "$TAR" -g "$HIST" * then gzip -8f "$TAR" # Función que comprueba el estado del backup e informamos checkBackup "$TAR".gz else checkBackup "error" fi # Si se ha devuelto un valor positivo para la verificación de la copia entonces eliminamos la anterior. Si # el valor devuelto es erróneo entonces no se elimina la anterior y se sigue con la siguiente copia if [ "$ESTADO" == "OK" ] then echo -e "Buscando backups de $DIR anteriores a este último full..." BACKOLD=`find "$DESTINO" -maxdepth 1 -type f -mmin +600 -name *_"$NOMBRE"_*.tar.gz |wc -l` if [ "$BACKOLD" -eq "0" ] then echo -e "No se han encontrado backups antiguos de $DIR para eliminar.\n" else find "$DESTINO" -maxdepth 1 -type f -mmin +600 -name *_"$NOMBRE"_* -exec rm -f {} \; && echo -e "Backups anteriores encontrados y eliminados.\n" fi else echo -e "El FullBackup anterior no será eliminado. Esta copia ha finalizado con errores\n" FIN="ERROR" fi ;; inc) PREFIJO="inc" TAR="$DESTINO"/"$PREFIJO"_"$NOMBRE"_"$EXT" compruebaDir "$DIR" echo -e "----------------------------------------------------------------------------------------------" echo -e "Iniciando backup INCREMENTAL para $DIR $(date "+[%x %X]")\n" cd "$DIR" if tar -cpvWf "$TAR" -g "$HIST" * then gzip -8f "$TAR" checkBackup "$TAR".gz "incremental" else checkBackup "error" FIN="ERROR" fi ;; dif) PREFIJO="dif" TAR="$DESTINO"/"$PREFIJO"_"$NOMBRE"_"$EXT" # Con este método también podemos recuperar el último campo (awk) TARFULL=`ls -l "$DESTINO" | grep full_"$NOMBRE" | awk '{printf $NF}'` # Sacamos la fecha para que respaldemos solo desde la última full hasta el momento FECHA=`date -r "$DESTINO"/"$TARFULL" "+%F %H:%M"` compruebaDir "$DIR" echo -e "----------------------------------------------------------------------------------------------" echo -e "Iniciando backup DIFERENCIAL para $DIR $(date "+[%x %X]")\n" cd "$DIR" if tar -cpvWf "$TAR" * -N "$FECHA" then gzip -8f "$TAR" checkBackup "$TAR".gz else checkBackup "error" FIN="ERROR" fi ;; esac } # Enviamos email con los resultados. Comprobamos el día para setear el primer parámetro y si hubieron errores # Con esta estructura condicional aprendemos a anidar if... elif.... else fi if [ "$DIA" == "$1" ] then BACKUP="FULLBACKUPS" elif [ "$DIA" == "$2" ] then BACKUP="DIFBACKUPS" else BACKUP="INCBACKUPS" fi if [ -z "$FIN" ] then CHECK="OK" else CHECK="ERROR" fi #Para terminar hacemos una copia del log en el directorio de logs antiguos cp "$LOG" "$OLDLOGS"/backup_"$(date +%d%b%y)".log enviaCorreo "$BACKUP - $CHECK" "$(cat $LOG)"
Descargarlo limpio de comentarios:
ATENCIÓN: El script del enlace está en formato doc. Una vez descargado eliminar la extensión o modificarla por .sh
4backup por nebul4ck se distribuye bajo una Licencia Creative Commons Atribución-NoComercial-CompartirIgual 4.0 Internacional.
Una pregunta:
¿Cómo restaurar los datos?
Una consideración:
buen artículo
Me gustaMe gusta
Buen detalle ese, quizás debería de haber creado un post seguido para despejar la duda. Al final desempaquetando el último full + diferenciales hasta el último día.
Un saludo y Gracias!!
Me gustaMe gusta
Genial. Muchas gracias.
Como aporte, he modificado las líneas 239, 273 y 293 para añadir patrones de exclusión -> if tar -cpvWf «$TAR» -g «$HIST» -X «${DESTINO}/exclude.txt» *
Me gustaMe gusta
Genial, buena idea, muchas gracias!!
Me gustaMe gusta
Hola,
Ante todo muchas gracias por el aporte,
Soy un poquito nuevo en linux, y hay ciertas cosas que no entiendo, he generado el scriptbackup.sh he copiado el script, y modificado donde quiero que me haga las copias, cuando ejecuto ./scriptbackup vie dom me indica todo el rato; (Especificar el dia para copias full y el dia para copias diferenciales (respectivamente).
El resto de dias se realizaran copias incrementales»)
No se si tengo que modificar algo más.
Quedo atento y gracias.
Me gustaMe gusta
Los días deben de estar en inglés, en su formato corto, no debería de tener problemas, comprueba eso. Un saludo
Me gustaMe gusta
Sigo sin poder hacer gz en los .tar .
Tuve que cambiar el idioma a español para que cogiera los dias, pense que ahi estaba el error y no..sigue sin comprimirlos y me colapsa el server..el log dice poco:
«tar: Exiting with failure status due to previous errors
EMERG: No se pudo verificar el archivador /media/dades/Backups/full_etc_06abr17.tar. Se recomienda revisar la última copia
El FullBackup anterior no será eliminado. Esta copia ha finalizado con errores
»
Las pruebas las estoy realizando desde 0, sin snapshots ni ningun backup anterior… Cuando ejecuto se crean bien los full de los dos path que indico, el log muestra todo compreso y la verificacion..el unico error este que te pongo.
Imagino que el script se esta quedando aqui…No entiendo ese if…$1 no es el primer dia de la semana que le pones al ejecutarlo? ./4backup.sh lun jue …lun=$1 , jue=$2¿?¿
ESTADO=»OK»
if [ «$1» == «error» ]
then
ESTADO=»ERROR»
echo -e «\nEMERG: No se pudo verificar el archivador $TAR. Se recomienda revisar la última copia\n»
elif [ «$1» == «$TAR».gz ]
Me gustaMe gusta
Has depurado ya 4backups?, no es complicado y te hará entender todo mejor y hacerte a los comandos. Es un script básico, si sigues la trazas y lo vas depurando, encuentras la solución.
Ánimo!!! Saludos
Me gustaMe gusta
Buenos días,
Lo primero felicitarte y darte las gracias por el script, es muy bueno, pero no entiendo muy bien como funciona, como puedo configurar los días de la copias full, diferencial e incremental?. Luego también quería saber como lo pongo en el crontab. no se si me he explicado bien , cuando yo lanzo el comando pongo sudo ./4backup ./backup lun y funciona pero cuando pongo ./4backup ./backup lun jue no funciona con lo cual si quiero que se haga la copias full dos dias no se hacerlo. hay algun sitio para configurarlo.?
gracias de antemano
Me gustaMe gusta
Hola Jaime, el programa se invoca tan solo con dos parámetros, el primero el día para las copias full y el segundo para las diferenciales e incrementales, según toque. Tu problema es que estás pasando dos parámetros ya y no te deja meter el tercero que es el de las copias diferenciales e incrementales. Debes de ejecutar el programa tal que así:
# ./4backups dia_full dia_diferencial
Por ejemplo: # ./4backups lun jue
Un saludo!
Me gustaLe gusta a 1 persona
excelente script amigo una pregunta podrías ayudarme a modificarlo un poco para que cuando haga los backup no haga compresión alguna?
Me gustaMe gusta
Hola Carlos!, a ver.. te puedo ayudar en algo pero es importante que lo hagas tu, ya te lleve 1 día, si lo que quieres es aprender, además de tener un «programa» que sabe lo que hace, algo que ayuda a la hora de detectar posibles errores.
Cosas que tienes que tener en cuenta:
Cada vez que aparece un comando gzip, deberías de eliminar esas líneas, por ejemplo:
gzip -8f «$TAR» -> Aquí se comprime el tar con un grado de 8. Como no quieres comprimir pues la eliminas.
gzip -vt «$TAR».gz -> Esta línea de la función «checkbackup» comprueba el archivo comprimido, como ya no se ha comprimido deberías de eliminarla. Aquí debes de tener en cuenta que tras la comprobación, existe la siguiente estructura lógica:
if [ «$?» -ne «0» ]
then
ESTADO=»ERROR»
else
Que lo que hace es comprobar que el comando anterior se ha ejecutado correctamente, como se ha eliminado y no se va a ejecutar, pues se elimina también estas líneas.
Otra cosa a tener en cuenta una vez eliminado todas las líneas donde se procede a comprimir o comprobar una compresión, es el nombre de los archivos, por ejemplo líneas como la siguiente deberían de ser corregidas, ya que ningún archivo terminará en gz:
elif [ «$1» == «$TAR».gz ]
ó
BACKOLD=`find «$DESTINO» -maxdepth 1 -type f -mmin +600 -name *_»$NOMBRE»_*.tar.gz
En las líneas anteriores deberías de quitar el .gz
Bueno amigo estos son algunos puntos a tener en cuenta, no es complicado, pero deberás de prestar atención al código, saber lo que hace y ejecutarlo con directorios de pruebas, hasta que te de buenos resultados.
Un saludo!!
Me gustaMe gusta
A mi me ocurre que no me los comprime, los deja solo en tar. Alguna idea?
El script es grandioso :D
Me gustaMe gusta
En el log no dice nada?, mira el log y dime que copias son las que no te comprimen, si las full, diferenciales… para echarle un vistazo cuando tenga un ratillo.
Un saludo!!
Me gustaMe gusta
hola, estoy intentado ejecutar este script en una Raspberri Pi con Raspbian instalado, pero al intentar ejecutarlo tanto en la linea LISTA_DIR como DIAS me salta el error «Syntax error: «(» unexpected».
Asi que cambio la linea
DIAS=(lun mar mié jue vie sáb dom)
por
DIAS=»lun mar mié jue vie sáb dom»
lo mismo para LISTA DIR.
luego intento ejecutar con
sudo sh copia.sh mié sab
y me salta es la ayuda y no funciona. ahora no se si es debido a esas comillas o estoy intentando ejecutar mal el script.
Ahora nose que mas hacer para saber si esto funciona o no.
Me gustaMe gusta
Hola Julian, lo que ocurre es que el shell de Raspbian no te está interpretando correctamente el script y esto se debe a que el script está desarrollado para ser ejecutado por Bash. En muchos sistemas Linux, Bash es un simple enlace simbólico a sh y aquí, igualmente podríamos encontrar errores gramaticales. No obstante esto va mas allá. Si vas a tu Raspbian (yo uso openelec, el cual está montado sobre Raspbian) y haces un «ls -l /bin |grep bash» verás que tu bash es un enlace simbólico a busybox. Busybox es un «todoenuno» el cual contiene compiladas numerosas herramientas de un sistema GNU/Linux y que se suele instalar en los sistemas Linux embebidos, es decir los que usa por ejemplo Android. Busybox no contiene todas las funcionalidades de las herramientas compiladas. Lo mas seguro es que bash en este caso se encuentre compilado dentro de BusyBox y no sea un bash como tal por lo que en resumidas cuentas este script no va a funcionar a menos que vayas adaptándolo según los inconvenientes que te vayas encontrando. Seguramente haciendo algo mucho mas básico, te llegue a funcionar y puedas respaldar tus archivos automáticamente. Un saludo!
Me gustaLe gusta a 1 persona
Ok, gracias por responder, ire rehaciendo el script, lo mas seguro que es tenga que dividirlo en varias partes y ejecutarlo a punta de cron para que me haga segun que dia el tipo de compresion.
Gracias por todo excelente blog.
Un saludo.
Me gustaMe gusta
Gracias a ti! Saludos
Me gustaMe gusta
ACTUALIZO, Si que funciona en una Raspberry Pi con Raspbian, lo he hecho de la siguiente forma por si en un futuro alguien lo necesita.
pi@raspberry ~ $ nano 4Backup
//pego el script que anteriormente he configurado con mis rutas de archivo, de resto todo funciona correctamente.
//le doy permisos de ejecucion
pi@raspberry ~ $ sudo chmod 777 4Backup
//ahora ejecutare el script para que haga las copias full los Lunes y las diferenciales los miércoles
// tanto el miercoles como el sabado lleban tilde mié y sáb
pi@raspberry ~ $ sudo ./script lun mié
y listo, ya esto lo meto a que se ejecute todos los dias con un crontab -e.
Muchas gracias Nebul por el script. eres un crack.
Me gustaMe gusta
Julian Andres, muchas gracias por tu aportación!! esperemos que a otros compañeros les sea útil. ¿ Puedes por favor ejecutar los siguientes comandos y pegar aquí la info?
$ cat /etc/release
$ uname -a
Gracias!!
Me gustaMe gusta
[…] 4backup : Programa para crear copias de seguridad (full, diferenciales e incrementales) marzo 20, 2015 […]
Me gustaMe gusta
[…] 4backup : Programa para crear copias de seguridad (full, diferenciales e incrementales) marzo 20, 2015 […]
Me gustaMe gusta