{"id":34,"date":"2017-06-25T20:38:00","date_gmt":"2017-06-25T20:38:00","guid":{"rendered":""},"modified":"2019-03-12T20:10:16","modified_gmt":"2019-03-12T20:10:16","slug":"backup-your-files-with-simple-bash-scripts","status":"publish","type":"post","link":"https:\/\/ntlx.org\/de\/2017\/06\/backup-your-files-with-simple-bash-scripts.html","title":{"rendered":"Backup Your Files with simple Bash Scripts"},"content":{"rendered":"<p>Ever lost data you stored on a usb drive just because it stopped working and you did not have a backup? How often did you promise yourself to set up a backup system so this will not happen again &#8211; just a few days before forgot you wanted to do so? You are not alone &#8211; so did I. Until a few months ago, when I decided to store my data on my own NAS, run by a RaspberryPi 3 and OwnCloud, to give me the feeling to have control over where my data is physically stored. On a USB drive below my desk. Without a popup reminding me, my Dropbox is running out of space.<\/p>\n<div>As hard drives tend to fail, I decided to put a backup system in place so the data is safe as long as only one of the two hard drives stops working. And this was quite easy, so I want to share the simple bash scripts I use to create incremental backups of my data.<\/div>\n<div><\/p>\n<h2>The Strategy<\/h2>\n<\/div>\n<div>First, here is the backup strategy I implemented:<\/div>\n<div>&nbsp;&#8211; Over 5 Years, I want to keep a backup of the data, as it was in the beginning of that year<\/div>\n<div>&nbsp;&#8211; Over the last year, I want to keep the first backup of each month<\/div>\n<div>&nbsp;&#8211; Over the last month, I want to keep the backup of every Monday<\/div>\n<div>&nbsp;&#8211; Over the last week (7 days), the backups of every day are kept<\/p>\n<\/div>\n<div>That sounds like a high amount of data to store. But it is not, if you use the rsync argument&nbsp;<i>&#8211;link-dest &lt;folder&gt;&nbsp;<\/i>which makes rsync create hard links in the target folder to the folder we pass as an <i>&lt;folder&gt; <\/i>argument, instead of creating actual copies of the source. So, only a bit more space than the actual copy in the beginning is needed for every new backup. That is the data that actually changed &#8211; hence the data we want to back up, plus some overhead for folders and the hard links.<\/div>\n<div><\/div>\n<div>Here is the command we can use to create such incremental backups with rsync:<\/p>\n<\/div>\n<div>\n<div style=\"background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;\">\n<table>\n<tbody>\n<tr>\n<td>\n<pre style=\"line-height: 125%; margin: 0;\">1<\/pre>\n<\/td>\n<td>\n<pre style=\"line-height: 125%; margin: 0;\">rsync -a --delete --link-dest <span style=\"color: #008800; font-weight: bold;\">${<\/span><span style=\"color: #996633;\">LASTDAYPATH<\/span><span style=\"color: #008800; font-weight: bold;\">}<\/span> <span style=\"color: #008800; font-weight: bold;\">${<\/span><span style=\"color: #996633;\">DATADIR<\/span><span style=\"color: #008800; font-weight: bold;\">}<\/span> <span style=\"color: #008800; font-weight: bold;\">${<\/span><span style=\"color: #996633;\">TODAYPATH<\/span><span style=\"color: #008800; font-weight: bold;\">}<\/span><br \/><\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<div><\/div>\n<div>This command creates a backup of <i>${DATADIR}<\/i> to <i>${TODAYPATH}<\/i> creating links of unchanged data to <i>${LASTDAYPATH}.<\/i><br \/><i><br \/><\/i><\/div>\n<div>\n<h2>The Scripts<\/h2>\n<\/div>\n<div>Such a command should now be executed every night using a cron job.<\/div>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;\">\n<table>\n<tbody>\n<tr>\n<td>\n<pre style=\"line-height: 125%; margin: 0;\"> 1<br \/> 2<br \/> 3<br \/> 4<br \/> 5<br \/> 6<br \/> 7<br \/> 8<br \/> 9<br \/>10<br \/>11<br \/>12<br \/>13<\/pre>\n<\/td>\n<td>\n<pre style=\"line-height: 125%; margin: 0;\"><span style=\"color: #888888;\">#!\/bin\/bash<\/span><br \/><br \/><span style=\"color: #996633;\">TODAY<\/span><span style=\"color: #333333;\">=<\/span><span style=\"color: #008800; font-weight: bold;\">$(<\/span>date +%Y-%m-%d<span style=\"color: #008800; font-weight: bold;\">)<\/span><br \/><span style=\"color: #996633;\">BACKUPDIR<\/span><span style=\"color: #333333;\">=<\/span>\/nas\/backup\/daily\/<br \/><span style=\"color: #996633;\">SCRIPTDIR<\/span><span style=\"color: #333333;\">=<\/span>\/nas\/data\/backup_scripts <br \/><span style=\"color: #996633;\">DATADIR<\/span><span style=\"color: #333333;\">=<\/span>\/nas\/data\/<br \/><span style=\"color: #996633;\">LASTDAYPATH<\/span><span style=\"color: #333333;\">=<\/span><span style=\"color: #008800; font-weight: bold;\">${<\/span><span style=\"color: #996633;\">BACKUPDIR<\/span><span style=\"color: #008800; font-weight: bold;\">}<\/span>\/<span style=\"color: #008800; font-weight: bold;\">$(<\/span>ls <span style=\"color: #008800; font-weight: bold;\">${<\/span><span style=\"color: #996633;\">BACKUPDIR<\/span><span style=\"color: #008800; font-weight: bold;\">}<\/span> | tail -n 1<span style=\"color: #008800; font-weight: bold;\">)<\/span><br \/><span style=\"color: #996633;\">TODAYPATH<\/span><span style=\"color: #333333;\">=<\/span><span style=\"color: #008800; font-weight: bold;\">${<\/span><span style=\"color: #996633;\">BACKUPDIR<\/span><span style=\"color: #008800; font-weight: bold;\">}<\/span>\/<span style=\"color: #008800; font-weight: bold;\">${<\/span><span style=\"color: #996633;\">TODAY<\/span><span style=\"color: #008800; font-weight: bold;\">}<\/span><br \/><span style=\"color: #008800; font-weight: bold;\">if<\/span> <span style=\"color: #333333;\">[[<\/span> ! -e <span style=\"color: #008800; font-weight: bold;\">${<\/span><span style=\"color: #996633;\">TODAYPATH<\/span><span style=\"color: #008800; font-weight: bold;\">}<\/span> <span style=\"color: #333333;\">]]<\/span>; <span style=\"color: #008800; font-weight: bold;\">then<\/span><br \/><span style=\"color: #008800; font-weight: bold;\">        <\/span>mkdir -p <span style=\"color: #008800; font-weight: bold;\">${<\/span><span style=\"color: #996633;\">TODAYPATH<\/span><span style=\"color: #008800; font-weight: bold;\">}<\/span><br \/><span style=\"color: #008800; font-weight: bold;\">fi              <\/span><br \/>rsync -a --delete --link-dest <span style=\"color: #008800; font-weight: bold;\">${<\/span><span style=\"color: #996633;\">LASTDAYPATH<\/span><span style=\"color: #008800; font-weight: bold;\">}<\/span> <span style=\"color: #008800; font-weight: bold;\">${<\/span><span style=\"color: #996633;\">DATADIR<\/span><span style=\"color: #008800; font-weight: bold;\">}<\/span> <span style=\"color: #008800; font-weight: bold;\">${<\/span><span style=\"color: #996633;\">TODAYPATH<\/span><span style=\"color: #008800; font-weight: bold;\">}<\/span> <span style=\"color: #996633;\">$@<\/span><br \/><span style=\"color: #008800; font-weight: bold;\">${<\/span><span style=\"color: #996633;\">SCRIPTDIR<\/span><span style=\"color: #008800; font-weight: bold;\">}<\/span>\/deleteOldBackups.sh<br \/><\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<div>The <b>data<\/b> hard drive is mounted to <i>\/nas\/data<\/i>, the <b>backup<\/b> hard drive is mounted to <i>\/nas\/backup<\/i>. Every day the backup scripts creates a backup of the data drive to the backup drive (in the folder <i>daily &#8211; <\/i>which might be a misleading name as we store all the backups in it).<\/p>\n<p>At the end of the script, we trigger another script deleting all the old backups, which are not needed anymore according to the backup strategy above.<br \/><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;\">\n<table>\n<tbody>\n<tr>\n<td>\n<pre style=\"line-height: 125%; margin: 0;\"> 1<br \/> 2<br \/> 3<br \/> 4<br \/> 5<br \/> 6<br \/> 7<br \/> 8<br \/> 9<br \/>10<br \/>11<br \/>12<br \/>13<br \/>14<br \/>15<br \/>16<br \/>17<br \/>18<br \/>19<br \/>20<br \/>21<br \/>22<br \/>23<br \/>24<br \/>25<br \/>26<br \/>27<br \/>28<br \/>29<br \/>30<br \/>31<br \/>32<br \/>33<br \/>34<br \/>35<br \/>36<br \/>37<br \/>38<br \/>39<br \/>40<br \/>41<br \/>42<br \/>43<br \/>44<br \/>45<br \/>46<\/pre>\n<\/td>\n<td>\n<pre style=\"line-height: 125%; margin: 0;\"><span style=\"color: #888888;\">#!\/bin\/bash<\/span><br \/><br \/><span style=\"color: #996633;\">BACKUPDIR<\/span><span style=\"color: #333333;\">=<\/span>\/nas\/backup\/daily\/<br \/><span style=\"color: #008800; font-weight: bold;\">function <\/span>listYearlyBackups<span style=\"color: #333333;\">()<\/span> <span style=\"color: #333333;\">{<\/span><br \/>        <span style=\"color: #008800; font-weight: bold;\">for <\/span>i in 0 1 2 3 4 5<br \/>                <span style=\"color: #008800; font-weight: bold;\">do <\/span>ls <span style=\"color: #008800; font-weight: bold;\">${<\/span><span style=\"color: #996633;\">BACKUPDIR<\/span><span style=\"color: #008800; font-weight: bold;\">}<\/span> | egrep <span style=\"background-color: #fff0f0;\">\"$(date +%Y -d \"<\/span><span style=\"color: #008800; font-weight: bold;\">${<\/span><span style=\"color: #996633;\">i<\/span><span style=\"color: #008800; font-weight: bold;\">}<\/span> year ago<span style=\"background-color: #fff0f0;\">\")-[0-9]{2}-[0-9]{2}\"<\/span> | sort -u | head -n 1<br \/>        <span style=\"color: #008800; font-weight: bold;\">done<\/span><br \/><span style=\"color: #333333;\">}<\/span><br \/><br \/><span style=\"color: #008800; font-weight: bold;\">function <\/span>listMonthlyBackups<span style=\"color: #333333;\">()<\/span> <span style=\"color: #333333;\">{<\/span><br \/>        <span style=\"color: #008800; font-weight: bold;\">for <\/span>i in 0 1 2 3 4 5 6 7 8 9 10 11 12<br \/>                <span style=\"color: #008800; font-weight: bold;\">do <\/span>ls <span style=\"color: #008800; font-weight: bold;\">${<\/span><span style=\"color: #996633;\">BACKUPDIR<\/span><span style=\"color: #008800; font-weight: bold;\">}<\/span> | egrep <span style=\"background-color: #fff0f0;\">\"$(date +%Y-%m -d \"<\/span><span style=\"color: #008800; font-weight: bold;\">${<\/span><span style=\"color: #996633;\">i<\/span><span style=\"color: #008800; font-weight: bold;\">}<\/span> month ago<span style=\"background-color: #fff0f0;\">\")-[0-9]{2}\"<\/span> | sort -u | head -n 1<br \/>        <span style=\"color: #008800; font-weight: bold;\">done<\/span><br \/><span style=\"color: #333333;\">}<\/span><br \/><br \/><span style=\"color: #008800; font-weight: bold;\">function <\/span>listWeeklyBackups<span style=\"color: #333333;\">()<\/span> <span style=\"color: #333333;\">{<\/span><br \/>        <span style=\"color: #008800; font-weight: bold;\">for <\/span>i in 0 1 2 3 4<br \/>                <span style=\"color: #008800; font-weight: bold;\">do <\/span>ls <span style=\"color: #008800; font-weight: bold;\">${<\/span><span style=\"color: #996633;\">BACKUPDIR<\/span><span style=\"color: #008800; font-weight: bold;\">}<\/span> | grep <span style=\"background-color: #fff0f0;\">\"$(date +%Y-%m-%d -d \"<\/span>last monday -<span style=\"color: #008800; font-weight: bold;\">${<\/span><span style=\"color: #996633;\">i<\/span><span style=\"color: #008800; font-weight: bold;\">}<\/span> weeks<span style=\"background-color: #fff0f0;\">\")\"<\/span><br \/>        <span style=\"color: #008800; font-weight: bold;\">done<\/span><br \/><span style=\"color: #333333;\">}<\/span><br \/><br \/><span style=\"color: #008800; font-weight: bold;\">function <\/span>listDailyBackups<span style=\"color: #333333;\">()<\/span> <span style=\"color: #333333;\">{<\/span><br \/>        <span style=\"color: #008800; font-weight: bold;\">for <\/span>i in 0 1 2 3 4 5 6<br \/>                <span style=\"color: #008800; font-weight: bold;\">do <\/span>ls <span style=\"color: #008800; font-weight: bold;\">${<\/span><span style=\"color: #996633;\">BACKUPDIR<\/span><span style=\"color: #008800; font-weight: bold;\">}<\/span> | grep <span style=\"background-color: #fff0f0;\">\"$(date +%Y-%m-%d -d \"<\/span>-<span style=\"color: #008800; font-weight: bold;\">${<\/span><span style=\"color: #996633;\">i<\/span><span style=\"color: #008800; font-weight: bold;\">}<\/span> day<span style=\"background-color: #fff0f0;\">\")\"<\/span><br \/>        <span style=\"color: #008800; font-weight: bold;\">done<\/span><br \/><span style=\"color: #333333;\">}<\/span><br \/><br \/><span style=\"color: #008800; font-weight: bold;\">function <\/span>getAllBackups<span style=\"color: #333333;\">()<\/span> <span style=\"color: #333333;\">{<\/span><br \/>        listYearlyBackups<br \/>        listMonthlyBackups<br \/>        listWeeklyBackups<br \/>        listDailyBackups<br \/><span style=\"color: #333333;\">}<\/span><br \/><br \/><span style=\"color: #008800; font-weight: bold;\">function <\/span>listUniqueBackups<span style=\"color: #333333;\">()<\/span> <span style=\"color: #333333;\">{<\/span><br \/>        getAllBackups | sort -u<br \/><span style=\"color: #333333;\">}<\/span><br \/><br \/><span style=\"color: #008800; font-weight: bold;\">function <\/span>listBackupsToDelete<span style=\"color: #333333;\">()<\/span> <span style=\"color: #333333;\">{<\/span><br \/>        ls <span style=\"color: #008800; font-weight: bold;\">${<\/span><span style=\"color: #996633;\">BACKUPDIR<\/span><span style=\"color: #008800; font-weight: bold;\">}<\/span> | grep -v -e <span style=\"background-color: #fff0f0;\">\"$(echo -n $(listUniqueBackups) |sed \"<\/span>s\/ \/<span style=\"background-color: #fff0f0; color: #666666; font-weight: bold;\">\\|<\/span>\/g<span style=\"background-color: #fff0f0;\">\")\"<\/span><br \/><span style=\"color: #333333;\">}<\/span><br \/><br \/><span style=\"color: #007020;\">cd<\/span> <span style=\"color: #008800; font-weight: bold;\">${<\/span><span style=\"color: #996633;\">BACKUPDIR<\/span><span style=\"color: #008800; font-weight: bold;\">}<\/span><br \/>listBackupsToDelete | <span style=\"color: #008800; font-weight: bold;\">while <\/span><span style=\"color: #007020;\">read <\/span>file_to_delete; <span style=\"color: #008800; font-weight: bold;\">do<\/span><br \/><span style=\"color: #008800; font-weight: bold;\">        <\/span><span style=\"color: #008800; font-weight: bold;\">rm <\/span><span style=\"color: #008800; font-weight: bold;\">${<\/span><span style=\"color: #996633;\">file_to_delete<\/span><span style=\"color: #008800; font-weight: bold;\">}<\/span><br \/><span style=\"color: #008800; font-weight: bold;\">done<\/span><br \/><\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>The idea of this script is to first list all the backups that should be kept, according to our strategy, and afterwards invert this selection to find out the ones to delete.<\/p>\n<p>And that&#8217;s it! Not much magic in creating incremental backups without needing too much space. My NAS is running these scripts every night since 10 months now, currently backing u<span style=\"background-color: white;\">p<span style=\"font-family: inherit;\">&nbsp;<span style=\"font-variant-ligatures: no-common-ligatures;\">607 Gigabytes. The backups currently take 630 Gigabytes.&nbsp;<\/span><\/span>Fi<\/span>nd the current version of my simple bash scripts in this GitHub repository:&nbsp;<a href=\"https:\/\/github.com\/NautiluX\/backup_scripts\" target=\"_blank\">https:\/\/github.com\/NautiluX\/backup_scripts<\/a>  <\/div>\n","protected":false},"excerpt":{"rendered":"Ever lost data you stored on a usb drive just because it stopped working and you did not have a backup? How often did you promise yourself to set up a backup system so this will not happen again &#8211; just a few days before forgot you wanted to do so? You are not alone&#8230; <a class=\"view-article\" href=\"https:\/\/ntlx.org\/de\/2017\/06\/backup-your-files-with-simple-bash-scripts.html\">Artikel ansehen<\/a>","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[26,15,12,9],"_links":{"self":[{"href":"https:\/\/ntlx.org\/de\/wp-json\/wp\/v2\/posts\/34"}],"collection":[{"href":"https:\/\/ntlx.org\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ntlx.org\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ntlx.org\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ntlx.org\/de\/wp-json\/wp\/v2\/comments?post=34"}],"version-history":[{"count":1,"href":"https:\/\/ntlx.org\/de\/wp-json\/wp\/v2\/posts\/34\/revisions"}],"predecessor-version":[{"id":70,"href":"https:\/\/ntlx.org\/de\/wp-json\/wp\/v2\/posts\/34\/revisions\/70"}],"wp:attachment":[{"href":"https:\/\/ntlx.org\/de\/wp-json\/wp\/v2\/media?parent=34"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ntlx.org\/de\/wp-json\/wp\/v2\/categories?post=34"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ntlx.org\/de\/wp-json\/wp\/v2\/tags?post=34"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}