{"id":26256,"date":"2022-06-21T20:11:40","date_gmt":"2022-06-22T03:11:40","guid":{"rendered":"https:\/\/www.podfeet.com\/blog\/?p=26256"},"modified":"2022-06-22T11:07:15","modified_gmt":"2022-06-22T18:07:15","slug":"hazel-automator-shell-script","status":"publish","type":"post","link":"https:\/\/www.podfeet.com\/blog\/2022\/06\/hazel-automator-shell-script\/","title":{"rendered":"My Hazel Script of Doom&#x2122;&#xfe0f;"},"content":{"rendered":"<p>In March of this year, I wrote a blog post entitled, <a href=\"https:\/\/www.podfeet.com\/blog\/2022\/03\/shortcut-nas-volume\" target=\"_blank\" rel=\"noopener\">\u201cI Have Made Fire \u2013 Shortcut with Shell Script to Mount a NAS Share\u201d<\/a>. I was so proud of my little self because I&#8217;d written my first shell script to solve a real problem and I&#8217;d used a Shortcut with it just to prove that I could, not because it was necessary.<\/p>\n<p>In May I wrote another article entitled<a href=\"https:\/\/www.podfeet.com\/blog\/2022\/05\/keychain-server-broken\/\" target=\"_blank\" rel=\"noopener\"> \u201cHow Did I Break Keychain Access for My Local Server?\u201d<\/a> in which I explained that the fragility of Shortcuts made my solution entirely unstable.<\/p>\n<p>In that second article, I explained how I tried to fix my solution and ended up somehow breaking my iCloud Keychain so that I couldn\u2019t mount servers without pushing a button that said \u201cConnect\u201d. In a few thousand words I explained that I found a \u201cdefaults write\u201d command that stopped the dreaded Connect button popup. I thought I was on the road to getting my original Hazel script to work.<\/p>\n<p>All of that is still true, but the story isn\u2019t over, because I still had to rewrite the script to include the functionality of the fragile Shortcut.  For more than a month now I&#8217;ve been spending hours and hours trying to get this to work. I&#8217;ve asked the community in Slack and @lbutler jumped in to help. He gave me an idea that ended up being pivotal to the solution. The script goes inside Hazel, and the developer, Paul Kim has also been very helpful.<\/p>\n<figure style=\"float: right; margin-left: 10px\"><img decoding=\"async\" src=\"https:\/\/www.podfeet.com\/blog\/wp-content\/uploads\/2022\/06\/Rubber-Duck-Kevin-on-Top-of-My-Display-1.jpg\" alt=\"Rubber Duck Kevin on Top of My Display\"  title=\"Rubber Duck Kevin on Top of My Display.jpg\" width=\"400 \" height=\"\"><figcaption style=\"text-align:center\">Rubber Duck Kevin on Top of My Display<\/figcaption><\/figure>\n<p>As I continued to run into trouble, I even tried Rubber Ducking the problem. This is a technique where you try to explain the problem to an inanimate object and the very act of trying to explain the problem helps you find the solution. Kevin Allder sent me a set of rubber ducks and I keep one on top of my monitor for just this purpose. I even named my rubber duck Kevin.  I explained the problem in detail to Kevin (the duck) but he wasn\u2019t much help.<\/p>\n<p>Finally, I convinced listener and real-life friend Ed Tobias to rubber duck the problem with me in a video session. He\u2019s a super nerd and does a lot of coding but he often tells me he\u2019s not a \u201creal\u201d programmer because he does more hackery to get things done. I think that <em>is<\/em> a real programmer! In any case, I knew that Ed would be the perfect person to work through the problem with, and hopefully I&#8217;d figure it out.<\/p>\n<p>I promise I won\u2019t actually talk through any code at all, and still walk you through why this was such a sticky problem. It\u2019s really a story of the inadequacies of the tools we have, and the constraints Apple has caused in the name of security.  In order to explain, let me remind you of the problem I&#8217;m trying to solve.<\/p>\n<h4>The Problem to be Solved<\/h4>\n<p>I use the awesome app Hazel from <a href=\"https:\/\/noodlesoft.com\/\">noodlesoft.com<\/a> to monitor my podcasting folders on my Mac. When the enclosed files are older than 2 weeks, Hazel whisks the files off of my internal drive and moves them to a share on my Synology.  This is a critical automation for me because I create around 4GB of audio files every single week. If I don\u2019t have Hazel cleaning up these files, I could easily run out of disk space and be in real trouble.<\/p>\n<p>My Hazel rule works great &#8230; as long as I&#8217;m on my home network. The minute I leave my house, Hazel complains that it can\u2019t find my Synology so it can\u2019t clean up the folder. I&#8217;m barraged with notifications because Hazel so desperately wants to do her job. Hazel does have an off switch, so for years, I&#8217;ve been turning her off when I&#8217;m away from home as soon as I start getting notifications that she can&#8217;t find my server.  That\u2019s annoying because that means I also have to remember to turn her back on when I get home.<\/p>\n<p>The other problem with turning Hazel off is she also watches some other folders that don\u2019t require me to be on my home network, so I&#8217;d like those rules to keep working. For example, she watches my Delete Me folder and throws away files when they pass a certain age. All those rules stop running if I turn Hazel off. Instead of turning all of Hazel off, I could also pause just the rules that talk to my server, but that means a lot of manual intervention on my part, defeating the purpose of automation, and of course, all of those servers have to be unpaused upon my return to my home network.<\/p>\n<p>I wanted to do one simple thing.  I wanted to tell Hazel that if I&#8217;m on my home network, mount the server, and move the files. If I&#8217;m not on my home network, don\u2019t even try to mount the server and just fail silently.<\/p>\n<h4>Conditional Section for Hazel<\/h4>\n<figure style=\"float: right; margin-left: 10px\"><img decoding=\"async\" src=\"https:\/\/www.podfeet.com\/blog\/wp-content\/uploads\/2022\/06\/Shell-script-just-checking-network.png\" alt=\"Shell script just checking network\"  title=\"Shell script just checking network.png\" width=\"500 \" height=\"\"><figcaption style=\"text-align:center\">Can You Ping It?<\/figcaption><\/figure>\n<p>The first piece of the puzzle was how to tell Hazel whether I&#8217;m on my home network. That was pretty easy. I figured out how to send one single ping to the IP of my Synology. If the ping returns, then I&#8217;m on my home network. If there is no reply to the ping, then I must be out galavanting around on other networks.<\/p>\n<p>Hazel has two distinct parts. The first part is the conditional section. My conditional statement for my podcasting files is,  \u201cIf a file is older than two weeks\u201d.  On @lbutler\u2019s advice, I put my ping to test if I&#8217;m on the network up in that conditional section. In Hazel, you get to choose whether any or all rules have been met, so I set it to all conditions. The conditional section then says, &#8220;If Hazel can ping the Synology AND has found a file that is older than two weeks&#8221;, then it is allowed to proceed to the next section where actions will take place.<\/p>\n<p>I was able to determine that the conditional section was working properly by using a built-in function of Hazel, even though I hadn&#8217;t yet set up the action section. You can choose a file and Hazel will tell you whether any or all of the rules have been matched, including my script to ping the server.  I was able to flip back and forth between my main network and my guest network where the Synology was unavailable, and Hazel behaved as expected.<\/p>\n<h4>Action Section of Hazel<\/h4>\n<p>The second half of Hazel is where you tell her what to action to take.  My goal for a script in the action section was pretty simple. We know that we\u2019re on the home network because we passed both of the conditional rules tests.<\/p>\n<p>The action statement should have been the easy part. I only needed Hazel to mount the share, and then move the old files to the server.  This is where I ran into the difficulty with Apple\u2019s security constraints.  While I can use \u2318-K to mount my server from the GUI, if I use the <code>mount<\/code> command from the Terminal or a shell script, I get a popup showing my credentials, but it requires me to push the button, even though my credentials are stored in iCloud Keychain. Obviously, if I&#8217;m trying to have a script run to do this, I can\u2019t have it pushing a GUI button.<\/p>\n<p>This is different from the problem I described in my post entitled, &#8220;<a href=\"https:\/\/www.podfeet.com\/blog\/2022\/05\/keychain-server-broken\/\" target=\"_blank\" rel=\"noopener\">How Did I Break Keychain Access for My Local Server?<\/a>&#8220;. In that case, I was just getting a \u201cdo you really want to connect?\u201d button but it wasn&#8217;t asking about my login credentials.<\/p>\n<p>I chatted with @lbutlr about the fact that I couldn\u2019t mount the server without this GUI interaction, and he suggested a different approach. He built a <em>very<\/em> simple Automator script to mount his server, and he thought it might work for me.  Automator is a drag-and-drop environment where you can also add scripts so it sounded like a good path<\/p>\n<figure style=\"float: right; margin-left: 10px\"><img decoding=\"async\" src=\"https:\/\/www.podfeet.com\/blog\/wp-content\/uploads\/2022\/06\/Automator-Error-if-Share-Already-mounted.png\" alt=\"Automator Error if Share Already mounted\"  title=\"Automator Error if Share Already mounted.png\" width=\"250 \" height=\"270\"><figcaption style=\"text-align:center\">Automator Error if Share Already Mounted<\/figcaption><\/figure>\n<p>Following @lbutlr\u2019s lead, I dragged in the Action called Get Specified Servers, where I typed in the SMB mount command for my server and it\u2019s share: <code>smb:\/\/192.168.4.95\/synodo-al-backup<\/code>. The second Action to drag in is simply called Connect to Servers.<\/p>\n<p>As @lbutlr promised, I can run that Automator script and my server share mounts with no popup from the GUI asking if I really want to connect. I was so happy! But my joy was short-lived.<\/p>\n<p>These two simple actions work perfectly unless the server share is not already mounted. If the server share is already mounted, Automator throws an error. Well, that&#8217;s just silly.<\/p>\n<p>I would need to tell Automator to only try to mount the server if it\u2019s not already mounted. That didn&#8217;t sound too hard. (Queue the foreboding music.)<\/p>\n<h4>Automator Isn\u2019t Logical<\/h4>\n<p>This is when I discovered a huge limitation of Automator: it has no logic capabilities whatsoever. It has no concept of \u201cif this then that.\u201d<\/p>\n<p>I should be able to work around that. I figured I just write a shell script to put between the Actions \u201cGet Specified Server\u201d and \u201cConnect to Server. In my script I\u2019ll put the logic:<\/p>\n<pre><code>If the server is not mounted\n    then mount the share\nIf the server is already mounted\n    do nothing\nfi\n<\/code><\/pre>\n<p>That\u2019s a pretty simple set of instructions, but it didn\u2019t work.  It turns out that you simply <em>have<\/em> to give the second Action, \u201cConnect to Server\u201d a valid server address. Since \u2013 no matter what \u2013 that last &#8220;Connect to Server&#8221; action will be executed, if you don&#8217;t pass it an address to which it can connect, it throws an error.<\/p>\n<h4>Chasing My Tail<\/h4>\n<p>At this point, I felt like I was chasing my own tail. For weeks I would try to get this to work from the shell script but I\u2019d get that darn GUI popup, so I\u2019d switch back to using Automator but then I\u2019d run into the already-mounted server error. I kept thinking if I could write a better script, I could get out of this.<\/p>\n<p>At one point, I took a drastic step and checked the Hazel log files in the Console App. Console is the place where madness thrives. I\u2019ve looked at log files so many times, and not once did I learn a darn thing.  But Paul Kim, developer of Hazel, actually writes out log files in a surprisingly human-friendly way.<\/p>\n<p>According to the Hazel logs, with my Automator script embedded inside Hazel, the \u201cConnect to Server\u201d Action was very confused. Rather than being passed the server IP and share name, somehow it was getting passed the name of the file that Hazel had found was older than two weeks.<\/p>\n<figure style=\"float: right; margin-left: 10px\"><img decoding=\"async\" src=\"https:\/\/www.podfeet.com\/blog\/wp-content\/uploads\/2022\/06\/Ignore-This-Actions-Input.png\" alt=\"Ignore This Action's Input\"  title=\"Ignore This Actions Input.png\" width=\"600 \" height=\"234\"><figcaption style=\"text-align:center\">Ignore This Action&#8217;s Input<\/figcaption><\/figure>\n<p>Developer Paul is super helpful and super smart so I emailed him about this. He explained that Hazel always passes the file name from the conditional section to the action section. Since my first action is Automator, it is the lucky recipient.  Paul went on to explain that Automator has an option on each action to ignore any input it receives.<\/p>\n<p>This was super important information to have, and nothing I was doing was going to work if I hadn\u2019t known to implement this solution, but it didn\u2019t change the tail-chasing I was doing between shell scripts and Automator to reach my <em>very<\/em> simple goal.<\/p>\n<p>Before I go on, I want to tell you a funny little thing. When I was reading Paul\u2019s console logs, I wrote in our Programming By Stealth channel in Slack, \u201cProof that I\u2019ve descended into this shared madness called programming. I just successfully interpreted an application\u2019s log files. I AM a developer!\u201d  Scott Willsey made me laugh out loud when he responded with \u201cClearly the app\u2019s developer forgot their lessons in log file obfuscation.\u201d<\/p>\n<h4>Rubber Ducking Works<\/h4>\n<p>After chasing my tail for weeks, it was time for my rubber ducking session with Ed Tobias. I wanted to keep the conversation at a high level, so to avoid digging into syntax,  I started a screenshare and simply typed out logic statements such as, &#8220;If share is not mounted, then mount share, else do nothing.&#8221;  Once he had the big picture of what I was trying to do, I demonstrated the two problems: the shell script causing the GUI popup, and the Automator script falling over in a heap if the server was already mounted.<\/p>\n<p>As we noodled the various issues with solutions I had tried, it suddenly hit me &#8211; there WAS a solution.<\/p>\n<p>If Automator can mount the server share as long as it\u2019s unmounted, maybe the first step in my script should be to <em>unmount<\/em> the share. When I told Ed my idea, I could not stop laughing at how my solution was stupid and yet also brilliant.<\/p>\n<p>When Ed and I were finished reworking the solution, the entire function of the script was to find out if the share was mounted, and if so unmount it, then pass along the SMB connection information to the Automator action to mount the share.<\/p>\n<p>We tested Hazel with my now very simple Automator\/shell script under three scenarios:<\/p>\n<ol>\n<li>When I was on my guest network &#8211; as expected it did nothing at all and I got no notifications<\/li>\n<li>On my real network with the server share not mounted &#8211; it mounted the share and moved the old file as expected<\/li>\n<li>On my real network with the server share mounted &#8211; the share briefly disappeared, reappeared and the old file moved as expected<\/li>\n<\/ol>\n<p>With one last giggle at the solution, we ended our screen share and I declared victory.<\/p>\n<h4>What Can Possibly Go Wrong?<\/h4>\n<p>I had my first chance to real-world test of whether Hazel would fail silently when off of my home network over the Father&#8217;s Day weekend. We surprised Steve\u2019s dad by appearing on his doorstep for Father\u2019s day (they live 4 hours away), and we stayed in a hotel using the free WiFi. My VPN from PIA kicked in, and over the weekend, Hazel had no complaints about not finding my server. I was very happy.<\/p>\n<p>But then as I was writing the very last line of this article, I had a thought.  Hazel\u2019s job is to run silently in the background keeping an eye on your folders and taking action according to the rules you set for it. What if I\u2019m at home working on a spreadsheet that\u2019s on my Synology, and Hazel finds a file that she needs to move? She\u2019s going to unmount the share and remount it just as instructed. That would cause an interrupt and possibly corrupt my file.<\/p>\n<h4>Automounter to the Rescue<\/h4>\n<figure style=\"float: right; margin-left: 10px\"><img decoding=\"async\" src=\"https:\/\/www.podfeet.com\/blog\/wp-content\/uploads\/2022\/06\/Automounter-Pro-Settings.jpg\" alt=\"Automounter Pro Settings\"  title=\"Automounter Pro Settings.jpg\" width=\"400 \" height=\"\"><figcaption style=\"text-align:center\">Automounter Pro Settings<\/figcaption><\/figure>\n<p>Sadly, I had to abandon my brilliant solution of unmounting and remounting my share before doing any file moves with Hazel.  Earlier while I was searching for a solution to this conundrum, I found a freemium app in the Mac App Store called Automounter.<\/p>\n<p>I think I do have a solution though. I\u2019m going to keep the rule that tells Hazel if I\u2019m not on my network, then don\u2019t do anything, but I\u2019m not going to even try to mount (or unmount) the shares. Instead I\u2019m going to run the $10 <a href=\"https:\/\/apps.apple.com\/us\/app\/automounter\/id1160435653?mt=12\" target=\"_blank\" rel=\"noopener\">Automounter App from PixelEyes and available in the Mac App Store<\/a>.  PixelEyes is a New Zealand company so they must be good.<\/p>\n<p>Automounter\u2019s entire reason for being is to keep server shares mounted.  If one disconnects for some reason it just brings it right back. I hadn&#8217;t embraced Automounter as my solution because the idea of having all these server shares from my Drobo and Synology mounted all the time cluttering up my desktop made me crazy.<\/p>\n<p>But I gave Automator a try anyway, and it has a very slick feature: it can hide mounted shares so you don\u2019t have to look at them on your desktop. They still show in the Finder sidebar so there\u2019s no problem accessing them, but they\u2019re not in your way.  I can choose to not worry my pretty little head about them, and yet they&#8217;ll already be mounted when I need them.<\/p>\n<p>I configured Automounter to mount my server shares and it worked just as it promised. I paid $10 for the Pro Settings which allows you to define custom mount points, such as \/Volumes, and you can also have applications or files launch when the share is mounted.<\/p>\n<figure style=\"float: left; margin-right: 10px\"><img decoding=\"async\" src=\"https:\/\/www.podfeet.com\/blog\/wp-content\/uploads\/2022\/06\/Automounter-SMB-Not-Available.png\" alt=\"Automounter SMB Not Available\"  title=\"Automounter SMB Not Available.png\" width=\"479 \" height=\"206\"><figcaption style=\"text-align:center\">Automounter SMB Not Available<\/figcaption><\/figure>\n<p>Now the question was how would it react when I went on travel? Would it start sending me notifications that it couldn\u2019t find the servers, just like Hazel?<\/p>\n<p>Nope! I checked Automounter from the hotel WiFi, and in the listing of the shares I had instructed it to keep mounted, it simply said \u201cSMB not available\u201d. It also says \u201cRules not Met\u201d at the top.<\/p>\n<h4>Bottom Line<\/h4>\n<p>While it still bothered me that I couldn\u2019t crack the code (literally) on this problem, I discovered Automounter and it essentially solved the symptoms so my Hazel rules will function and not nag me when I&#8217;m away from my home network.<\/p>\n<p>But then I got an email from Ed saying he had another idea for a script\u2026<\/p>\n<p>And then I found in Paul&#8217;s excellent documentation that Hazel has a built-in Connect to Server feature that I forgot about\u2026<\/p>\n<p>&#x1f3b6; This is the song that never ends\u2026 &#x1f3b6;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In March of this year, I wrote a blog post entitled, \u201cI Have Made Fire \u2013 Shortcut with Shell Script to Mount a NAS Share\u201d. I was so proud of my little self because I&#8217;d written my first shell script to solve a real problem and I&#8217;d used a Shortcut with it just to prove [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":26262,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[147],"tags":[1032,885,5017,5346,5345,5347,1033],"class_list":["post-26256","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-posts","tag-automator","tag-hazel","tag-mount","tag-mount-nas","tag-mount-server","tag-mount-share","tag-shell-script"],"jetpack_featured_media_url":"https:\/\/www.podfeet.com\/blog\/wp-content\/uploads\/2022\/06\/Rubber-Duck-Kevin-on-Top-of-My-Display-1.jpg","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.podfeet.com\/blog\/wp-json\/wp\/v2\/posts\/26256","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.podfeet.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.podfeet.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.podfeet.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.podfeet.com\/blog\/wp-json\/wp\/v2\/comments?post=26256"}],"version-history":[{"count":8,"href":"https:\/\/www.podfeet.com\/blog\/wp-json\/wp\/v2\/posts\/26256\/revisions"}],"predecessor-version":[{"id":26268,"href":"https:\/\/www.podfeet.com\/blog\/wp-json\/wp\/v2\/posts\/26256\/revisions\/26268"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.podfeet.com\/blog\/wp-json\/wp\/v2\/media\/26262"}],"wp:attachment":[{"href":"https:\/\/www.podfeet.com\/blog\/wp-json\/wp\/v2\/media?parent=26256"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.podfeet.com\/blog\/wp-json\/wp\/v2\/categories?post=26256"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.podfeet.com\/blog\/wp-json\/wp\/v2\/tags?post=26256"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}