# *Shell script* para *backup* de arquivos
Documento elaborado para a disciplina **DS010 - Administração de Sistemas**, do curso de Análise e Desenvolvimento de Sistemas (TADS), da Universidade Federal do Paraná (UFPR), ministrada pelo Prof. Dr. Mauro Antônio Alves Castro. A equipe é formada pelos alunos:
- Daphne Spier Moreira Alves
- João Pedro Martins de Paula
- Leonardo Xavier da Silva Moraes
O objetivo desse tutorial é explicar o processo de criação e execução de *backups* de arquivos em sistemas *Linux*, levando em conta a sincronização de arquivos e a execução do *backup* em intervalos regulares.
## Explicando o *script*
O arquivo que será detalhado em seguida é o [**setup_backup.sh**](https://drive.google.com/file/d/1EJ2No5dS-P8gqgSkHCIbrUODuQSC1ght/view?usp=sharing).
### Passo 1: identifica o interpretador
A primeira linha é um *shebang* (```#!```) e identifica o tipo de *script* que estamos criando. Ele serve para que seu computador identifique qual programa roda aquele *script*.
```bash=1
#!/bin/sh
```
### Passo 2: função para escolher origem e destino
Nas linhas 3 a 11 é criada a função que selecionará os diretórios de origem (aquele que o usuário quer fazer o backup) e destino (aquele onde deseja-se salvar o backup).
O comando ```printf``` imprimirá na tela do usuário o texto que perguntará qual as pastas de origem e destino ele quer selecionar.
O comando ```read``` vai salvar a resposta do usuário nas variáveis ==destino_backup== e ==origem_backup==.
Na linha 9, serão mostradas as opções selecionadas pelo usuário e será perguntado se ele confirma as opções escolhidas. O usuário deverá digitar ==Y== para **sim** ou ==n== para **não**.
```bash=3
ask_for_origin_destiny(){
//Seção onde são definidos Origem e Destino
printf "Digite a ORIGEM (desde a raiz): \n";
read origem_backup;
printf "Digite o DESTINO (desde a raiz): \n";
read destino_backup;
printf "Origem: $origem_backup\nDestino: $destino_backup\nConfirma?(Y/n)\n";
read conf;
}
```
### Passo 3: função para escolher diretório de criação do *script*
Nas linhas 13 a 22 é criada a função que pergunta onde o usuário quer criar o *script* de *backup*. O usuário pode escolher um diretório ou deixar em branco para que o arquivo seja criado no diretório onde está o arquivo **setup_backup.sh**
```bash=13
destino_script_backup(){
echo -e "Onde deseja que este backup seja gerado (O script)?\n Certifique-se de passar o endereço desde a raiz.\n Deixe em branco para gerar em $diretorio_atual"
read destino;
if [[ ! -z $destino ]]; then
target=$destino/backup_$nome.sh
else
// Pega o diretório onde este script está localizado e concatena no endereço o nome do arquivo onde realmente estarão os comandos de backup
target=$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )/$nome.sh
fi
}
```
Na linha 20 é usado um comando um pouco mais complexo, que será destrinchado a seguir:
1. ```BASH_SOURCE[0]``` retorna o caminho de onde o script está sendo executado, relativo à pasta atual do terminal;
2. ```dirname``` retorna o nome da pasta que contém o objeto passado à ele, no caso, o nome do script obtido com o ```BASH_SOURCE[0]```;
3. O ```cd``` caminha para a pasta devolvida por ```dirname```;
4. ```>``` direciona toda a saída de ```stdout``` para ```/dev/null```, que é o _void_ do computador. Tudo que é direcionado para ```/dev/null``` é automaticamente excluído;
5. ```2>``` por sua vez executa o mesmo que o item 4, porém a saída redirecionada é ```stderr``` e o destino é o descritor 1, ou seja, ```stdout```. Resumindo, jogamos fora quaisquer _outputs_ normais e os erros direcionamos para a saída padrão;
6. ```&& pwd``` executa ```pwd``` logo em seguida, para então obter o diretório definitivo de onde está o script atual. Por fim, concatena-se um ```/$nome.sh``` para criar o arquivo de destino do *script* de *backup*.
### Passo 4: define a frequência de execução do *backup*
Nas linhas 24 a 36 é perguntado ao usuário qual o intervalo de tempo em que ele deseja realizar o *backup*, utilizando os comandos ```printf```e ```read```.
```bash=24
//Seção onde define a frequência com a qual o script será executado
printf "Digite a frequência com que deseja que o backup seja feito";
printf "\nO que for digitado será passado diretamente para o comando crontab, portanto é possível criar intervalos, divisões od tipo */num e etc...";
printf "\n Digite o MINUTO: ";
read min;
printf "\n Digite a HORA: ";
read hora;
printf "\n Digite o DIA DO MÊS: ";
read dia_mes;
printf "\n Digite o MÊS DO ANO: ";
read mes_ano;
printf "\n Digite o DIA DA SEMANA (0 a 6 sendo domingo a sábado): ";
read dia_sem;
```
Nas linhas 38 a 42 existe um laço para certificar que o usuário confirmou as opções de origem e destino. Enquanto ele não digitar ==y== ou ==Y== continuará sendo impressa na tela a pergunta.
```bash=38
ask_for_origin_destiny;
while [[ $conf != "Y" && $conf != "y" ]]; do
clear;
ask_for_origin_destiny;
done
```
### Passo 5: define diretório de criação do *script*
Nas linhas 44 a 53, pergunta-se para o usuário qual o nome ele quer dar para o arquivo de *backup*. Isso possibilita a configuração de vários *scripts* com origens e destinos diferentes. Também é pedido o caminho onde se deseja guardar o _script_ de backup. Este caminho não pode ser igual à origem ou ao destino do *backup*.
```bash=44
echo "Nome do backup (isto permite que você configure vários backups com origens e destinos diferentes)";
read nome;
diretorio_atual=`pwd`;
destino_script_backup;
while [[ $destino == $destino_backup || $destino == $origem_backup ]]; do
clear;
echo "Você não pode colocar o script de backup no seu destino/origem!";
destino_script_backup;
done
````
### Passo 6: uso do Crontab
```bash=53
(crontab -l 2>/dev/null; echo "$min $hora $dia_mes $mes_ano $dia_sem bash $target") | crontab -;
````
O comando ```crontab``` permite especificar hora e dia de execução do *backup*, onde:
- **mm** = minuto (0-59)
- **hh** = hora (0-23)
- **dd** = dia (1-31)
- **MM** = mês (1-12)
- **ss** = dia da semana (0-7)ou *sun*, *mon*, *tue*, *wed*, *thu*, *fri* e *sat*. Os números **0** e **7** representam o domingo
- **[-l]:** lista os horários agendados.
1. Em qualquer posição, o ==*== (asterisco) pode ser usado como caractere curinga.
2. Em qualquer posição pode-se especificar intervalos usando com o caractere ==-== (hífen)
3. Em qualquer posição pode-se especificar listas usando com o caractere ==,== (vírgula)
Da mesma forma que no passo 3, o comando ```2>/dev/null``` filtrará os erros para que eles não sejam gerados no console.
### Passo 7: explicando criação do *script* de *backup* e uso do *rsync*
Nas linhas 63 a 75 é criado o *script* de *backup*, imprimindo com ```echo```, em um arquivo já especificado anteriormente, os comandos necessários.
O comando ```rsync```transfere e sincroniza arquivos ou diretórios de forma eficiente entre uma máquina local, servidor remoto ou qualquer outro do tipo.
- **[v] verbose**, aumenta nível de informação passada para o usuário;
- **[u] update**, somente sincroniza se for mais novo;
- **[m] prune-empty-dirs**, remove cadeias de diretórios vazios da lista de arquivos;
- **[a] archive**, copia os arquivos e diretórios recursivamente - como **[r]** - e preserva links simbólicos, permissões de arquivos, propriedades de usuário e grupo (ownership) e timestamps.
- **[-exclude]**: marca para não sincronizar esse arquivo;
- **[--delete-excluded]**: Apaga os arquivos que não estão mais no diretório de origem. Ou seja, se a ideia é manter uma cópia fiel do diretório de origem, essa opção é necessária para não haver arquivos que não serão utilizados ocupando espaço no disco.
- **[-progress]**: Exibe o progresso da transferência;
**IMPORTANTE** Como o comando ```--delete-excluded``` pode ser perigoso, por apagar arquivos que não estão no diretório de origem, o *script* também pergunta ao usuário (linhas 60 a 61) se ele realmente deseja isso.
```bash=60
echo -e "Último passo! Deseja que o script realize um espelhamento no destino (Y/n)?\n(apaga no destino pastas que não existem na origem)";
read opc;
//Cria o script de backup com os endereços já definidos
echo "#!/bin/sh" >> $target;
echo >> $target;
echo "cp $destino_backup/logBackup.txt $origem_backup/log.txt 2>/dev/null;" >> $target;
if [[ $opc == 'y' || $opc == 'Y' ]]; then
echo "rsync -vuma --log-file=$origem_backup/log.txt --exclude=logBackup.txt --delete-excluded --progress $origem_backup/ $destino_backup/" >> $target;
else
echo "rsync -vuma --log-file=$origem_backup/log.txt --exclude=logBackup.txt --progress $origem_backup/ $destino_backup/" >> $target;
fi
echo "cat $origem_backup/log.txt >> $destino_backup/logBackup.txt;" >> $target;
echo "echo "-------------------------------------------" >> $destino_backup/logBackup.txt;" >> $target;
echo "rm $origem_backup/log.txt" >> $target;
echo "rm $destino_backup/log.txt" >> $target;
```
### Passo 8: exemplo de *script* de *backup* criado
Quando o usuário rodar o *script* **setup_backup.sh** será criado o *script* de execução do *backup* na pasta que ele escolheu.
Nesse exemplo, o *script* possui o seguinte código:
```bash=1
#!/bin/sh
cp /home/daphne/Documents/logBackup.txt /home/daphne/Desktop/log.txt 2>/dev/null;
rsync -vruml --log-file=/home/daphne/Desktop/log.txt --exclude=logBackup.txt --delete-excluded --progress /home/daphne/Desktop/ /home/daphne/Documents/
cat /home/daphne/Desktop/log.txt >> /home/daphne/Documents/logBackup.txt;
echo ----------------------- >> /home/daphne/Documents/logBackup.txt;
rm /home/daphne/Desktop/log.txt
rm /home/daphne/Documents/log.txt
```
Aqui os valores indicados pelo usuários são escritos no lugar das variáveis ==origem_backup== e ==destino_backup==.
## Executando o *script*
Para executar o *script* de *backup*, o usuário deverá abrir uma janela do terminal e digitar o comando ```cd```mais o caminho da pasta onde está o arquivo **setup_backup.sh**.
Em seguida, deve-se digitar o seguinte comando para executar o *script*:
````
bash setup_backup.sh
````
> É necessário dar as devidas permissões ao *script* antes de executá-lo
Dessa forma ele conseguirá configurar os diretórios de criação do *script* de *backup*, a origem e destino do *backup*, assim como a frequência com que deseja executá-lo.
A figura abaixo mostra um exemplo de execução de *backup* de minuto em minuto:

Nesse exemplo, o diretório de origem é o ==Desktop==, o de destino é o ==Documents== e o *script* de *backup* ==teste_backup== foi criado no diretório atual do terminal ==Downloads==, onde se encontrava o arquivo **setup_backup.sh**.
E pronto, o *script* de *backup* será executado na frequência e diretórios indicados pelo usuário! :wink:
[ToC]