10:32pm
It has been a little while since I wrote a post here, but like my newsletter, I often only make a post (or send an email newsletter) when I have something that people who follow me may find interesting.
If you pay any attention to the categories of my posts, you’ll see I’ve categorized this one under “Tech Geekery” and “Random Thoughts.” You’ll see why I selected “Tech Geekery” in a minute… and… well… I tend to ramble a bit as I write these, so everything is categorized under “Random Thoughts.”
Seems like a bit of fair warning, there…
Right, then. Moving on!
I’m sure this may come as something of a shock to you, but I’m a firm believer in backups. I don’t keep anything I value on a thumb drive, because my experience says they can (and will) drop their file tables at the drop of a hat. Now, I personally have never experienced this (probably because I always eject my thumb drives properly), but I have seen it happen… to a professor of one of my classes while I was at the Cisco Networking Academy in Parkersburg, WV. This was back in 2005, so I will freely admit that modern thumb drives may not be as prone to the problem.
I write my stories in Scrivener, and yes, I’m almost 100% Apple in my tech. I still have a Windows box for gaming, but I have to admit that gaming doesn’t have as much appeal as it used to. Is that because I’m changing? Or because modern games aren’t quite up to the same quality as their forebears? Like Paul Harrell liked to say, you be the judge.
But I digress…
Scrivener and pretty much any cloud storage app (i.e. Dropbox, Sync, Google Drive, et al) do not play well together. Matter of fact, some of the cloud storage apps will corrupt your Scrivener files during the upload process. It’s been a minute or two (read that as ‘year or two’) since I read the help file for cloud sync apps from Scrivener’s developers, but based on what I know of how these apps work and the nature of the Scrivener file, I suspect that the ultimate cause is that Scrivener has an auto-save feature that continually saves whatever you type/change as you type/change it.
I have never had a Scrivener file get corrupted, but when I was working out of my Dropbox folder, I did notice that Scrivener would often be as much as three lines behind what I was typing, meaning that I would have large chunks of text suddenly appear on the screen.
I hope it goes without saying that such behavior was not ideal.
I didn’t like moving my Scrivener files out of Dropbox, because one time, I lost all my fiction when I decided I would finish the Linux-to-Windows migration while I was fevered. However, I disliked the text chunking I was experiencing even more.
Since Scrivener has its own backup system built-in, I figured I could recreate my Scrivener files if I ever lost them. Those backup files are ZIP archives and have no problems being written straight to Dropbox.
Still, though… I’ve always been a “belt and suspenders” kind of guy, meaning that I try to follow the “If you have one, you have none” mentality even when it comes to my backup methods.
Since macOS is based on Linux (and has been since Apple started using Intel processors with the advent of OS X, back around 2000 – 2002), I started wondering why I couldn’t just write a shell script and set up a cron job that would copy my Scrivener files on a schedule I’m comfortable with.
Like so many aspects of my life, that particular thought vanished into the shadowy recesses of my mind, but today (well… tonight), I can tell you it is very possible.
Now, there are a couple caveats to discuss that could easily become gotchas if you would like to recreate this on your Mac. Admittedly, these are more on the geeky side of things, so feel free to copy my script below, make the changes necessary to match it to your Mac, and ignore this section.
#1: Since macOS Catalina (I think?), macOS has not used bash for its shell. It has used and still uses zsh. By and large, this won’t affect your scripting beyond the very first line, but it’s important to be aware of that if you’ve used bash a lot and try a 1:1 port of what you know.
#2: macOS defaults to VIM for its shell-based text editor. I personally prefer nano, but it’s not worth the time and effort to install it, set it to default, and proceed. VIM is very different from nano, so you might want to look up common VIM commands on the internet and have it ready. When it came to writing my script, I used Sublime Text and only relied on VIM for editing the crontab.
Speaking of Sublime Text, it’s an excellent text editor that you can use for everything from a plain text Notepad (Windows) or TextWrite (macOS) replacement to typing up code files, such as HTML or XML. I highly recommend it.
Okay. So, here’s the text of my copy script…
#!/bin/zsh
Date=$(date +”%Y-%m-%d”)
mkdir /Users/rob/”Dropbox (Personal)”/Fiction/”Scrivener Files from Mac SSD”/$Date
cp -rf /Users/rob/”Scrivener Files”/* /Users/rob/”Dropbox (Personal)”/Fiction/”Scrivener Files from Mac SSD”/$Date
chown -R rob:staff /Users/rob/”Dropbox (Personal)”/Fiction/”Scrivener Files from Mac SSD”/$Date
chmod -R 755 /Users/rob/”Dropbox (Personal)”/Fiction/”Scrivener Files from Mac SSD”/$Date
I would normally have blocked out the precise file path, but you’ll have to change it to match yours anyway.
So, let’s step through this line by line.
#!/bin/zsh
This first line tells the computer that we’re building a zsh script. This is necessary.
Date=$(date +”%Y-%m-%d”)
The second line defines the variable ‘Date,’ which is necessary for the rest of the script. The text inside the parenthesis sets a value for the ‘Date’ variable as a string of text that equals [Year]-[Month]-[Day of Month], or 2024-11-08 as of this writing.
mkdir /Users/rob/”Dropbox (Personal)”/Fiction/”Scrivener Files from Mac SSD”/$Date
The third line creates a folder under my “Scrivener Files from Mac SSD” folder and uses the variable ‘Date’ that we created in the second line of the script as the name of the folder. For any folder that has a space in the name, you must enclose that folder name in quotation marks, like I did above.
Hrmmm… I probably should’ve put this in the Caveats section up above, but Linux (and therefore macOS) uses the forward slash “/” to progress down a folder tree, unlike Windows. Windows uses the backslash “\”.
I really didn’t like putting the period outside the closing quote on the backslash example, but the geek in me didn’t like the idea that someone might think the period belonged with the backslash and tried scripting Windows like this, “C:\.Users\.” I’m not sure if that would do anything to Windows beyond erroring out, but I shudder at the thought of it.
cp -rf /Users/Rob/”Scrivener Files”/* /Users/rob/”Dropbox (Personal)”/Fiction/”Scrivener Files from Mac SSD”/$Date
This next line copies everything under my “Scrivener Files” folder to the folder created in the previous line of the script. The “-rf” is an option for the copy command (cp) that stands for Recursive and Force. This option is necessary for my script, because I organize my Scrivener files into folders named after my series.
chown -R rob:staff /Users/rob/”Dropbox (Personal)”/Fiction/”Scrivener Files from Mac SSD”/$Date
chmod -R 755 /Users/rob/”Dropbox (Personal)”/Fiction/”Scrivener Files from Mac SSD”/$Date
The final two lines set the permissions of the new folder and all of its contents. In Linux (and therefore macOS), the permissions are based on the concept of Read-Write-Execute for User, Group, and Anonymous. In the line with “chown,” I set the owners of the folder and its contents to be my user account (rob) and the staff user group. In the line with “chmod,” I set the permissions to be 755, giving my user account full permissions (Read/Write/Execute) and then Read & Execute permissions for the group (staff) and anonymous users.
Do I need to give “staff” and anonymous users Read & Execute permissions? Probably not. I’m pretty sure the Dropbox app is running as my user, but I don’t care enough to confirm that. I do not know what other processes on the Mac require “staff” permissions to work correctly, so I’d rather play it safe… even if it might violate the principle of Least Privilege.
During the writing of the script, I tested it at both the “mkdir” and the “cp” lines. I did that simply by saving my script, setting it to executable, and running it after writing each line.
Oh! Important note! If you want to use this as a guide to create your own automated backup, you need to save your script file as [script-name].sh, where [script-name] is whatever you want to name the file. You’ll see mine below. The file extension “.sh” tells the computer that it’s a shell script.
From here on out, you will need to use the Terminal app on your Mac. You can find it by opening Finder, clicking on Applications in the left-hand sidebar, double-clicking on the Utilities folder, and then double-clicking on the Terminal app icon.
I used Terminal to make my shell script executable with this command:
chmod 755 /Users/rob/ScrivenerBackup.sh
This is a necessary step to ensure that cron can actually run the script.
Then, it was a simple matter of adding the two final lines to ensure the permissions were properly set, and all that remained was to set up the cron job.
“cron” is the Linux equivalent of Task Scheduler in Windows, and you set up a cron job by typing this command:
crontab -e
Now, unless you type “sudo” in the front of that, the cron job will be set up to run with your user account, which means only files or folders your user account natively controls can be worked with. I don’t need elevated permissions for this script, so running it on my Mac under my user account will be just fine.
I have to say… cron jobs have one of the oddest configuration methods I’ve ever encountered. On one hand, it makes perfect sense and allows for an impressive amount of customization and fine-tuning. But on the other hand, it just looks weird. Here’s the configuration for my Scrivener Backup cron job:
0 0 1 */1 * /bin/zsh /Users/rob/ScrivenerBackup.sh
See what I mean?
These cron jobs are formated like this: [Minute] [Hour] [Day] [Month] [Weekday] [command to run]. In the above example, I have selected for the command to run at the zero minute of the zero hour (midnight) of the first day of every month. At least, I think that’s what I’ve coded. I’ll know for certain when I wake up on December 1st.
The command calls “/bin/zsh” to run the shell script “/Users/rob/ScrivenerBackup.sh” at the timing I’ve specified.
The more I think about it, you might want to wait for me to make a second post about this on December 1st before trying this on your own… just to be sure I coded the timing correctly. 😉
Alrighty. If you’ve made it this far, thanks for sticking with me. I hope you found some part of this interesting or valuable.
I wish you and yours all the best, and stay safe out there.
0 Comments