A client, working in health care, had a special request: each working day she was filling in forms in a database with patient data. After the day was finished she wanted a back-up. In general, a back-up of an in use database is a bad idea. She was using Windows with a custom database application.
So what I wanted was a shortcut to a batch file for starting the database and as a next step – after closing the database – calling the back-up procedure. That is the nice thing about a batch file, it starts the next step only when the previous step is finished and if the next step is after a day work, so be it.
If you just want a button with a back-up script, it is even easier, read on. Anyway, this example back-ups to a local drive, an attached USB-drive and Google drive.
I learned from this exercise myself and I thought it might be a good idea to share it, the techniques are very useful in general.
Table of Contents
Ingredients
Now if you say back-up, I say rsync. Rsync is incredible powerfull GPL software used all over the world for mission critical smart file copying. See https://en.wikipedia.org/wiki/Rsync. The challenge is to get that typical Unix stuff working under Windows. So I say Cygwin, which gives you a Linux command line inside Windows, another challenge solved. See https://en.wikipedia.org/wiki/Cygwin.
These are the main ingredients, but some additional programs are needed too. For communication with the user, some pop ups are wanted. A nice and easy to use utility for this is “The Wizard’s Apprentice”, see http://wizapp.sourceforge.net/. That is all, except for one, caveat. Because you use Linux on Windows you may run into trouble when editing text files. It boils down to the following for every enter given in a text file: Think of old type writers, end of line markers in Windows are CR (Carriage Return) and LF (Line Feed), LF in Unix and CR when using an Apple. So we need to edit files in an end of line marker aware program, and that is where Notepad++ comes up. See https://notepad-plus-plus.org/.
Finally your own scripts are part of the ingredients as well, so you can make a nice distribution of the complete bunch.
Installing
Base
Installing everything is pretty straight forward:
- Cygwin can be downloaded and installed, follow instructions.
- During installation of packages, search for rsync and install that too.
- The Wizard’s Apprentice: just put the executable and the rest in a folder.
- Notepad++: Follow instructions.
Considering the external USB drive: don’t use drive letters because they can change. Use a mount point inside your c: drive, mount it to a folder like c:\mnt\extdrive.
The Desktop shortcut
This is tricky when you want to run a program from a batch file, normally you’ll get a bonus in the form of a DOS-window. It is hard to avoid that window and if you search the net you’ll find many examples of how to solve that. However, so far there is only one that has been practical useful. Your shortcut should be something like
C:\Windows\System32\wscript.exe "C:\bin\sitapps\invisible.vbs" "C:\bin\sitapps\runapps.bat" |
If you don’t have wscript, get it from Microsoft. What is inside invisible.vbs? This:
CreateObject("Wscript.Shell").Run """" & WScript.Arguments(0) & """", 0, False |
Now no DOS-windows is launched and runapps.bat will run after opening your shortcut. So what has runapps.bat in store?
The solution
Now it starts to become interesting. About runapps.bat:
1 2 3 4 5 6 7 8 | rem Where is script located... set sitloc=C:\bin\sitapps rem Main application start /wait "C:\Program Files (x86)\YourProgram\program.exe" c:\data\database\yourdatabase rem Follow up application - remove start for hidden %sitloc%\followup.bat rem start %sitloc%\followup.bat rem pause |
So this is basically the real work flow, offering you your program and after that a file called followup.bat is fired up. Yes it is a chain of commands. Let’s list followup.bat
1 2 3 4 5 6 7 | cd set cygwinbinloc=C:\cygwin64\bin set sitlocposix=/cygdrive/c/bin/sitapps set PATH=%cygwinbinloc%;%PATH% %cygwinbinloc%\bash.exe %sitlocposix%/bash1.sh pause exit |
The back-up script
So followup.bat clears the way for Cygwin by setting some variables and then launches bash1.sh. That is where the real action occurs as shown below. The first line of this file should start with #!/bin/bash.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | #!/bin/bash ## (c) 2015,2016 NedCAD ##### DO NOT RUN BEFORE SETTING EVERYTHING RIGHT !!!!! (YOU MIGHT LOSE DATA) ##### # See manual... ## Setting vars, watch trailing slashes... # Location to backup from without trailing slash, NO SPACES, make junctions # Junctions: mklink /j "c:\dir 0\some spaces" "c:\dir1\nospaces" strsrcloc="/cygdrive/c/data/database /cygdrive/c/data/versleuteld" strsrcgdr="/cygdrive/c/data/database" strsrcrem="/cygdrive/c/data" strsrcremstat="/cygdrive/c/data" # Locations w.o. trailing slash to backup to, local, googledrive, removable # MAKE NEW AND UNIQUE DIRECTORIES FOR THE BACK-UPS!!! destloc=/cygdrive/c/backup/back-up_sit destgdr=/cygdrive/c/gdrive/back-up_sit destrem=/cygdrive/c/mnt/backup1/back-up_sit destremstat=/cygdrive/c/mnt/backup1/back-up_sit/static # Delete older than n days, l local, o online, r removable (values like 30 for 30 days, -1 to remove all back-ups) deloldloc=30 deloldgdr=7 deloldrem=180 # Location sitapps sitapps=/cygdrive/c/bin/sitapps # End of variables printf "=====================\n" > backuplog.txt printf "StringIT back-up-log\n" >> backuplog.txt strdate=$(date -Idate) printf "Backup date stamp: $strdate\n" >> backuplog.txt printf "=====================\n" >> backuplog.txt cd "$sitapps" printf "Current directory, sitapps = $sitapps\n" >> backuplog.txt printf "Delete local after days, deloldloc = $deloldloc\n" >> backuplog.txt printf "Delete online after days, deloldgdr = $deloldgdr\n" >> backuplog.txt printf "Delete removable after days, deloldrem = $deloldrem\n" >> backuplog.txt printf "Dirs to local back-up, strsrcloc = $strsrcloc\n" >> backuplog.txt printf "Dirs to Google Drive back-up, strsrcgdr = $strsrcgdr\n" >> backuplog.txt printf "Dirs to Rem. Drive back-up, strsrcrem = $strsrcrem\n" >> backuplog.txt printf "Dirs to Rem. Dr. Static, strsrcremstat = $strsrcremstat\n" >> backuplog.txt printf "Local destination, destloc = $destloc/$strdate\n" >> backuplog.txt printf "On line destination, destgdr = $destgdr/$strdate\n" >> backuplog.txt printf "Removable destination, destrem = $destrem/$strdate\n" >> backuplog.txt printf "Removable destination, Static,destremstat = $destremstat\n" >> backuplog.txt ## Making dirs printf "\nMaking dirs\n" >> backuplog.txt # Dir name is date stamp Iso # Check if dirs exist if [ ! -d "$destloc" ]; then printf "$destloc does not exist.\n" >> backuplog.txt actions/checkdirs.bat exit fi if [ ! -d "$destgdr" ]; then printf "$destgdr does not exist.\n" >> backuplog.txt actions/checkdirs.bat exit fi if [ ! -d "$destrem" ]; then printf "$destrem does not exist.\n" >> backuplog.txt actions/checkdirs.bat exit fi if [ ! -d "$destremstat" ]; then printf "$destremstat does not exist.\n" >> backuplog.txt actions/checkdirs.bat exit fi # Make dirs mkdir -v "$destloc/$strdate" >> backuplog.txt 2>&1 mkdir -v "$destgdr/$strdate" >> backuplog.txt 2>&1 mkdir -v "$destrem/$strdate" >> backuplog.txt 2>&1 ## Do the dance # rsync printf "\n=====================\nSyncing to $destloc/$strdate\n=====================\n" >> backuplog.txt rsync -avhHPs --del --progress --no-compress --log-file=./backuplog.txt $strsrcloc "$destloc/$strdate" printf "\n=====================\nSyncing to $destgdr/$strdate\n=====================\n" >> backuplog.txt rsync -avhHPs --del --progress --no-compress --log-file=./backuplog.txt $strsrcgdr "$destgdr/$strdate" printf "\n=====================\nSyncing to $destrem/$strdate\n=====================\n" >> backuplog.txt rsync -avhHPs --del --progress --no-compress --log-file=./backuplog.txt $strsrcrem "$destrem/$strdate" printf "\n=====================\nSyncing to $destremstat (Static)\n=====================\n" >> backuplog.txt rsync -avhHPs --del --progress --no-compress --log-file=./backuplog.txt $strsrcremstat "$destremstat" ## Keep it clean (delete old back-ups) printf "\n=====================\nDeleting n days old back-ups,\nn = $deloldloc local, $deloldgdr online, $deloldrem removable\n=====================\n" >> backuplog.txt find "$destloc" -mindepth 1 -maxdepth 1 -type d -ctime +$deloldloc >> backuplog.txt #if [ $? -eq 0 ];then printf "Dirs found in $destgdr.\n"; else echo "nothing";fi find "$destloc" -mindepth 1 -maxdepth 1 -type d -ctime +$deloldloc -exec rm -rf {} \+ >> backuplog.txt find "$destgdr" -mindepth 1 -maxdepth 1 -type d -ctime +$deloldgdr >> backuplog.txt find "$destgdr" -mindepth 1 -maxdepth 1 -type d -ctime +$deloldgdr -exec rm -rf {} \+ >> backuplog.txt find "$destrem" -mindepth 1 -maxdepth 1 -type d -ctime +$deloldrem >> backuplog.txt find "$destrem" -mindepth 1 -maxdepth 1 -type d -ctime +$deloldrem -exec rm -rf {} \+ >> backuplog.txt printf "\n\nEND OF LOG FILE\n" >> backuplog.txt # Change log to DOS (default commented) # sed -i 's/$/\r/' backuplog.txt actions/finish.bat # If all is fine comment the last line (or not...) read -p "Druk op [Enter] om af te sluiten..." |
There is a lot to tell about the actions above but most things are quite obvious. Just search the net for strings that are not clear to you. I am sorry to be so brief here but one can write books about BASH scripting.
Final pieces
At the part where directory locations are checked there is a command – when failure – saying actions/checkdirs.bat. This is where the wizzard’s apprentice comes into action. Checkdirs.bat looks like:
1 2 3 4 | set watitle=IMPORTANT! set watext=One or more destination folders are not found.~~No back-up is made!~~Check if your disk is connected.~Check settings and or contact your organization. cd start /w wa\wizapp MB INFORMATION |
Another is near the end: actions/finish.bat, as follows:
1 2 3 4 5 6 7 8 | set watitle=Back-up is finished set watext=Back-up procedure is finished.~It is useful to check the log file of the last back-up from time to time.~~Click OK to view the log file.~~Click Cancel to close the program. start /w wa\wizapp MB QUES if errorlevel 2 exit if errorlevel 0 if not errorlevel 2 goto show_log :show_log start firefox backuplog.txt rem pause |
That is about it. I hope it inspires you to build something like this or copy paste the whole or parts of it.