# *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: ![](https://i.imgur.com/EA5crYt.jpg) 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]