jpt: jamf examples

jpt has some practical applications for the Jamf admin

Sanitizing Jamf Reports

Let’s say you’ve exported an Advanced Search, it’s got some interesting data you’d like to share, however there is personal data in it. Rather than re-running the report, why not blank out or remove those fields?

Here’s a sample file: advancedcomputersearch-2-raw.json

This an excerpt from one of the computer record:

{
"name": "Deathquark",
"udid": "2ca8977b-05a1-4cf0-9e06-24c4aa8115bc",
"Managed": "Managed",
"id": 2,
"Computer_Name": "Deathquark",
"Last_Inventory_Update": "2020-11-04 22:01:09",
"Total_Number_of_Cores": "8",
"Username": "Professor Frink",
"FileVault_2_Status": "Encrypted",
"JSS_Computer_ID": "2",
"Number_of_Available_Updates": "1",
"Model_Identifier": "MacBookPro16,1",
"Operating_System": "Mac OS X 10.15.7",
"Model": "MacBook Pro (16-inch, 2019)",
"MAC_Address": "12:34:56:78:9A:BC",
"Serial_Number": "C02K2ND8CMF1",
"Email_Address": "frink@hoyvin-glavin.com",
"IP_Address": "10.0.1.42",
"FileVault_Status": "1/1",
"Processor_Type": "Intel Core i9",
"Processor_Speed_MHz": "2457",
"Total_RAM_MB": "65536"
}

Now let’s say the privacy standards for this place is GDPR on steroids and all personally identifiable information must be removed, including serials, IPs, UUIDs, almost everything (but you don’t want to run the report again because it took ages to get the output)!

Here’s what that command would look like: jpt -o replace -v '"REDACTED"' -p '$["advanced_computer_search"]["computers"][*]["name","udid","Username","Computer_Name","MAC_Address","Serial_Number","Email_Address","IP_Address"]' ./advancedcomputersearch-2-raw.json

Here’s what that same computer looks like in the resulting output (as well as all others in the document):

  {
    "name": "REDACTED",
    "udid": "REDACTED",
    "Managed": "Managed",
    "id": 2,
    "Computer_Name": "REDACTED",
    "Architecture_Type": "i386",
    "Make": "Apple",
    "Service_Pack": "",
    "Last_Inventory_Update": "2020-11-04 22:01:09",
    "Active_Directory_Status": "Not Bound",
    "Total_Number_of_Cores": "8",
    "Username": "REDACTED",
    "FileVault_2_Status": "Encrypted",
    "JSS_Computer_ID": "254",
    "Number_of_Available_Updates": "1",
    "Model_Identifier": "MacBookPro16,1",
    "Operating_System": "Mac OS X 10.15.7",
    "Model": "MacBook Pro (16-inch, 2019)",
    "MAC_Address": "REDACTED",
    "Serial_Number": "REDACTED",
    "Email_Address": "REDACTED",
    "IP_Address": "REDACTED",
    "FileVault_Status": "1/1",
    "Processor_Type": "Intel Core i9",
    "Processor_Speed_MHz": "2457",
    "Total_RAM_MB": "65536"
  }

What we did was use the -o replace operation with a -v <value> of the JSON string "REDACTED" to all the paths matched by the JSONPath union expression (the comma separated property names in brackets) of the -p option. JSON Pointer can only act on one value at a time, this is where JSONPath can save you time and really shines.

jpt is fast because WebKit’s JavascriptCore engine is fast, for instance there is a larger version of that search that has 15,999 computers, it took only 11 seconds to redact every record.

jpt -l $.advanced_computer_search.computers ./advancedcomputersearch.json
15999

time jpt -o replace -v '"REDACTED"' -p '$.advanced_computer_search.computers[*]["name","udid","Username","Computer_Name","MAC_Address","Serial_Number","Email_Address","IP_Address"]' ./advancedcomputersearch.json > /dev/null 

11.14s user 0.35s system 104% cpu 11.030 total

Put Smart Computer Groups on a diet

When you download Smart Groups via the API, you will also get an array of all the computers objects that match at that moment in time. If you just want to back up the logic or upload to another system, you don’t want all those computers in there.

Sample file: smartgroup-1-raw.json

{
  "computer_group": {
    "id": 12,
    "name": "All 10.15.x Macs",
    "is_smart": true,
    "site": {
      "id": -1,
      "name": "None"
    },
    "criteria": [
      {
        "name": "Operating System Version",
        "priority": 0,
        "and_or": "and",
        "search_type": "like",
        "value": "10.15",
        "opening_paren": false,
        "closing_paren": false
      }
    ],
    "computers": [
      {
        "id": 1,
        "name": "mac1",
        "mac_address": "12:34:56:78:9A:BC",
        "alt_mac_address": "12:34:56:78:9A:BD",
        "serial_number": "Z18D132XJTQD"
      },
      {
        "id": 2,
        "name": "mac2",
        "mac_address": "12:34:56:78:9A:BE",
        "alt_mac_address": "12:34:56:78:9A:BF",
        "serial_number": "Z39VM86X01MZ"
      }
    ]
  }
}

Lets’ remove those computers with jpt and the JSON Patch remove operartion:
jpt -o remove -p '$.computer_group.computers' ./smartgroup-1-raw.json

Since the target is a single property name, JSON Pointer can also be used:
jpt -o remove -p /computer_group/computers ./smartgroup-1-raw.json

{
  "computer_group": {
    "id": 12,
    "name": "All 10.15.x Macs",
    "is_smart": true,
    "site": {
      "id": -1,
      "name": "None"
    },
    "criteria": [
      {
        "name": "Operating System Version",
        "priority": 0,
        "and_or": "and",
        "search_type": "like",
        "value": "10.15",
        "opening_paren": false,
        "closing_paren": false
      }
    ]
  }
}

Further Uses

Using only JSON Patch replace and remove operations this JSON could have its id removed to prep it for an API POST on another JSS to create a new group or the name and value could be modified in a looping script to create multiple JSON files for every macOS version. The jpt is flexible enough to handle most anything you throw at it, whether you are using the standalone version or have embedded it in your scripts.

Stop by the jpt GitHub page to get your copy

secret origins: the jpt

On building a JSON tool for macOS without using Python, perl, or Ruby.

In my work as a Macintosh engineer and administrator I’ve noticed macOS has lacked a bundled tool for working with JSON at the command line. Where XML has its xpath, if your shell script needs some JSON chops, it’ll require an external binary like jq or something else scripted in Python, Ruby or perl using their JSON modules. The problem is, those runtimes have been slated for removal from a future macOS. So I took that as challenge to devise a method to query and modify JSON data within shell scripts, that didn’t use one of those deprecated scripting runtimes and didn’t require an external binary dependency either. Could I achieve robust and native JSON parsing on a Mac by simply “living off the land”?

Why not just re-install the runtimes when Apple deprecates them?

“Why limit yourself like this? Just re-install the runtimes and move on”, you may ask. Well, I’d like to think that limitations can inspire creativity but we should also consider there may be some other reasons why Apple is discontinuing the inclusion of those runtimes. Some may say, “Apple Silicon + macOS 11.0 is the perfect time for them to clean house”, to which I’d have to agree that’s a very good reason and likely a factor. Others could say they are looking to tighten the screws to keep out unsigned code: maybe, they do like to glue things shut! But really, I think it’s more akin to the web-plugins of yore like Java and Flash. Apple does not want to be the conduit for deploying 3rd party party runtimes which increase the attack surface of macOS. This seems like the most reasonable of the explanations. So, if you accept that Apple is attempting to reduce attack surface, why increase it by re-installing Python, Ruby, or perl, just so a transient script (like a Jamf Extension Attribute) can parse a JSON file? My answer to that, is you don’t. You play the hand you’re dealt. Game on!

Looking for truffles (in a very small back yard)

Despite having another project (shui) that can output and invoke Applescript from within a shell scripts for generating user interfaces, I definitely knew that Applescript was not the way to go. Apple however, added to the languages Open Scripting Architecture (OSA) supports back in 2014 with OS X Yosemite (10.10), they added Javascript along with a bridge to the Cocoa Objective-C classes and they called it JXA: Javascript for Automation. This seemed like a promising place, so I started playing with osascript and figured out how to load files and read /dev/stdin using JXA, and while looking for an answer for garbled input from stdin I came upon a Japanese blog that mentioned jsc the JavaScriptCore binary which resides in the /System/Library/Frameworks/ JavaScriptCore.framework. Arigato! Pay dirt! 🤑 jsc does exactly what we need it to do: It can interpret Javascript passed as an argument, can access the filesystem and read from /dev/stdin, and best of all is in non-Private System level Framework that exists all the way back to OS X 10.4! Just the kind of foundation on which to build the tool.

Homesteading jsc

The existence of jsc goes back all the way to OS X Tiger and it’s functionality has evolved over the years. In order to have a consistent experience in jpt from macOS 10.4 – 11.x+ a few polyfills had to be employed for missing functions, along with a few other workarounds regarding file loading, printing and exit codes (or lack thereof). Once those were addressed the jsc proved to be a highly optimized Javascript environment that’s blazingly fast. It spans 13 macOS releases and is even present in many Linux distros out-of-the-box (Ubuntu and CentOS) and can even be run on Windows when the Linux Subsytem is installed.

With the host environment sorted, I began working with the original JSONPath code by Stefan Goessner as the query language. I didn’t know about JSON Pointer yet so this strange beast was all I knew! It worked out really. I went full throttle into developing the “swiss army knife” of JSON tools. I really leaned into the Second System Effect, as described in the Mythical Man-month, it’s when you put every doodad, gizmo and doo-hicky in your 2nd product (my first simple JSON pretty-printer built in JS on jsc). Eventually though, after I reached a feature plateau, I came back around to the address the quirks of the original JSONPath code. I ended up rewriting signifigant chunks of JSONPath and released it as it’s own project: brunerd JSONPath. But I digress, let’s get back to the JSON Power Tool.

jpt: powers and abilities

At it’s most basic, jpt will format or “pretty print” any JSON given. jpt can also handle data retrieval from JSON document using either JSONPath or JSON Pointer syntax. JSONPath, while not a standard, is a highly expressive query language akin to XPath for XML, with poweful features like recursive search, filter expressions, slices, and unions. JSON Pointer on the other hand is narrower in focus, succint and easily expressed, and standardized but it does not offer any of the interogative features that I feel make JSONPath so intriguing. Finally, jpt can also modify values in a JSON document using standardized JSON Patch operations like: add, remove, replace, copy, move, and test, as well as the also standardized JSON Merge Patch operation. Altogether, the jpt can format, retrieve and alter JSON documents using only a bit of outer shell script plus a lot more Javascript on any macOS since 10.4! 😅

Where can I get the jpt?

Stop by the project’s GitHub page at: https://github.com/brunerd/jpt

There you will find the full source and also a minified version of the jpt for inclusion within your shell scripts. Since it’s never compiled you can always peer inside and learn from it, customize it, modify it, or just tinker around with it (usually the best teacher).

Future Plans

There will undoubtedly be continued work on the jpt. Surely there are less than optimal routines, un-idiomatic idioms, edge cases not found, and features yet to be realized. But as far as the core functionality goes though, it’s fairly feature complete in my opinion. Considering that one of my top 10 StrengthsFinder qualities is “Maximizer”, the odds are pretty good, I’ll keep honing the jpt‘s utility, size (smaller), and sharing more articles with examples on the kinds of queries and data alteration operations the jpt can so perform. Stay tuned!

New Projects: jpt and shui, Now Available

Between March and October 2020 I had some great ideas for command line Mac utilities the MacAdmin could apprecite and I had the time to devote to their realization. I’m excited to present these two open source projects, available on GitHub: jpt and shui. I hope they can add richness to your shell scripts’ presentation and capabilities without requiring additional external dependancies.

jpt – the “JSON Power Tool” is a Javascript and shell script polyglot that leverages jsc, the JavascriptCore binary that is standard on every Mac since 10.4 and since the jpt is purposefully written in ES5 to maintain maximum compatibility, why yes, this tool does run on both PPC and Intel Macs all the way back to OS X Tiger and then all the way forward to the latest 11.0 macOS Big Sur! Many Linux distros like CentOS and Ubuntu come with jsc pre-installed also, even Windows with the Linux Subsystem installed can run jsc and therefore can run the jpt!

What you can do with the jpt? Query JSON documents using either the simple yet expressive JSONPath syntax or the singular and precise JSON Pointer (RFC6901) syntax. The output mode is JSON but additional creative output modes can render JSONPaths, JSON Pointer paths, or even just the property names with their “constructor” types (try -KC with -J or -R) Textual output can be encoded in a variety of formats (hex/octal/URI encoding, Unicode code points, etc…), data can be modified using both JSON Patch (RFC6902) operations (add, replace, remove, copy, move, test) and also JSON Merge Patch (RFC7386) operations. JSON can be worked with in new ways, try -L for “JSONPath Object Literal” output to see what I mean. Or you simply feed jsc a file to pretty-print (stringify) to /dev/stdout. I’ll be writing more about this one for sure.
Github project page: jpt
Tagged blog posts: scripting/jpt

shui – first-class Applescript dialog boxes in your shell scripts without needing to remember esoteric Applescript phrasings! If you think it’s odd for code to have possessive nouns and are more comfortable in shell, you’re not alone. shui can be embedded in either bash or zsh scripts but it can also output Applescript if you really want to know how the sausage is made or want to embed in your script without shui. Hopefully shui will let you forget those awkward Applescript phrasing and focus on your shell script’s features and functionality. It uses osascript to execute the Applescript and launchctl to invoke osascript in the correct user context so user keyboard layouts are respected (vs. root runs). Check out the project page for demo videos and then give shui a try.
Project page: shui
Tagged blog posts: scripting/shui

Track and Tackle com.apple.macl

UPDATE: As of Big Sur (macOS 11) the com.apple.macl extended attribute can be removed using: xattr -rd com.apple.macl <file/folder>


Starting in macOS Catalina, an extended attribute (XA) named com.apple.macl is being added to the files and folder you work with. What does it do? How does it work? When does it get added? Where is the documentation?

All these are good questions, but there’s no official documentation. A good start is this assemblage of articles, here’s the highlights from that page and a few more…

Apple talked about this vaguely in their WWDC 2019 session 701, “Advances in macOS Security“, the Files and Folders fun starts at 21:21 but there’s nothing really about the implementation

Tom Bridge talked about com.apple.macl to the Penn State Macadmins back in August before Catalina’s release. He’s one of the first to notice this XA (and talk about it). Although, he says that curl will add this XA to downloads, it thankfully does not. Most likely it happened when he dragged it to Terminal or performed some other action on it.

Howard Oakley of the prolific Eclectic Light Company blog kicks the tires a bit more and finds some interesting quirks, as he always does.

Jeff Johnson though, really hits the nail on the head in illustrating what’s going on when you simply drag and drop a file or folder into Terminal and com.apple.macl is appended. He dropped this, December 18th and well… the holidays were around the corner and we all had better stuff to do!

It’s 2020 now and time to come back around to this. A very helpful post in the Apple Dev forums from an Apple employee, Quinn “the Eskimo”, decodes the data structure a bit more as well as what some of the conditions for having this XA appended are:

When a user selects a “protected” file or folder in an NSOpenPanel in a non-sandboxed app on Catalina, consent is inferred and the app can access it.

It seems that 01 00 is a header of some form and … is a UUID associated with my test app.  I dug into how that UUID is set up and, well, it’s complex, and more of an implementation detail than I care to go into here on DevForums.

https://forums.developer.apple.com/thread/124121

Well, I would have loved the complex implementation detail! But I’ll tell you this: The app UUID in com.apple.macl is unique to EVERY computer. Jeff Johnson was right when he said: “The macl is effectively untraceable“.

However you can begin to see the shape of things when you write a tool to output the UUID in CSV like I have. Behold maclTrack.command

Run it in Terminal, give it files or folders as arguments, it’ll report on them, you can even specify a maximum depth for folders (-d) and silence reporting on items lacking com.apple.macl (-s), pipe it into tee to see the output and write it to a file.

This poor file got clobbered by multiple app UUIDs

Something interesting I noticed from using this tool is seeing the “Header” as Quinn called it, differ from 0100 to 0200. I believe 0200 is for drag and drop operations vs. a regular save which results in 0100.

Again, the app UUIDs you see on the files on your Mac will never be seen on another Mac ever – why? Because UUIDs are meant to be truly unique. Have you read the man page for uuidgen? They ain’t eff’in around!

 The uuidgen command generates a Universally Unique IDentifier (UUID), a 128-bit value guaranteed to be unique over both space and time.

Apple man page for uuidgen

So what’s the point of having a long lived XA that you can’t get rid of and that’s only useful on your local Mac? If it’s a permissions granting XA (the opposite of com.apple.quarantine which imposes restrictions) then why not let the user remove it? In some ways it’s a cross computer file tracking mechanism, albeit “anonymized”. Perhaps allowing it’s removal though (aka writing “nothing” to the XA) also allows for something to be written? This is where I could get in trouble speculating, it’s just a guess.

Perhaps you’ll forgive me that, since I have found a way to clear com.apple.macl without disabling SIP! Zip it. No, not the one spawned Finder’s “Compress” menu item, I mean /usr/bin/zip! I noticed command line zip would obliterate XAs during a project many years ago and it still does my friends! So behold maclTackle.command

This script has considerably less engineering to it because it’s a PoC and I’m really don’t want to take responsibility for something going awry should you try to clear com.apple.macl on your entire Desktop folder (no I haven’t tested that). But it will strip the XAs off a file that’s for sure! NOTE: USE A NON-IMPORTANT FILE with this script (make a duplicate). The script will overwrite the original (don’t like the behavior? It’s a script: Comment it out!)

🎶 Gonna wash that macl right outta my file… 🎶

Apple may come along and close this loophole now that it’s been pointed out or perhaps they won’t since they’d have to publish the modfication? Perhaps they’ll continue to purge “undesirable” binaries from macOS? They say they will stop shipping Python, perl and ruby in macOS some day and you know what xattr is written in? That’s right Python! ¯\_(ツ)_/¯

Alright, come and get it while the gettin’s good! Thanks for reading!

Time to Die: When Mac app and package certificates expire

So I had a Draft about this last week but an usure feeling. I thought: “Do I really have anything to contribute? Folks are already aware of this. What can I really add?” So, I let it slide, then Thurday came and there was a hiccup: Apple hadn’t refreshed all their packages! Oh noes. Could this have been something my script could have helped head off? No but it might have made examining the packages Thursday afternoon a bit easier. So with that, I present certChecker.command

I’d advise just copy pasting the raw script into a new BBEdit document and saving it as “certChecker.command“. The extension matters, BBEdit will set the executable bit and you won’t have to mess with cleaning off the com.apple.quaratine flag as you would if you downloaded it.

Not much of a looker at the command line but comes together nicely in Quick Look

The need to find expired packages is critical if you use Jamf Pro to deploy your packages. macOS will throw an error if you try and install a pkg with an expired cert via the installer command line tool. It will suggest you use the -allowUntrusted flag, however this is not an option with Jamf Pro. You either have to get a new resigned package or repack it. Repacking is basically expanding the pacakge then flattening it again. This will strip out all certs. repackPKGs.command is useful for when vendors have forgotten to issue new packages and you need a working package.

Cert validation caveat: some packages can expire yet remain valid! These are signed with “trusted time stamps” and Suspicious Package explains this quite well. Their tool can better help you assess what a package will do when it’s expiration occurs. For certChecker.command I use pkgutil’s assesment for package files but for apps all we have is the the “not after” date.

Speaking of apps: the Install macOS Mojave.app from the App Store was giving me the “damaged” message on Thursday around 1PM CST…

The application certs were all VALID, my script said so, what gives? Doing a bit of sleuthing revealed InstallESD.dmg to be different between the two installers. I used find to run a md5 on all the files in each installer and send the output to two files. Running find from inside the apps keeps the base paths the same and allows for easy comparison with diff or XCode’s FileMerge (a favorite).

#compare two apps in Terminal
cd <ye olde installer app>
find . -type f -exec md5 {} \; | sort > ~/Desktop/YE_OLDE.txt
cd <new installer app>
find . -type f -exec md5 {} \; | sort > ~/Desktop/NEWNESS.txt
cd ~/Desktop
diff YE_OLDE.txt NEWNESS.txt

FileMerge inside XCode.app is a great visual diff’er
Turns out we have some stowaways in the old installer.
All is well, app cert and InstallESD packages are looking good.

By ~3pm CST they’d gotten everything fixed and the packages inside were all good. Not since the great expiration of 2015, have we had to care about expiring Apple packages. This time Apple has only pushed the app certs out 1 1/2 years to April 12th, 2021. That’s not long! The packages inside InstallESD.dmg are good however until April 14, 2029. Which one will win? Will we care (will we be able to roll back?) I’ll leave that as an exercise/rhetorical question for someone else.

In the meantime you might have other packages lurking in your distribution points that need updating or repacking. If so give certChecker.command a whirl to ferret them out. I hope it saves you some time and effort <insert reference to Roy Batty and “tears in the rain here” :>

macOS shell games: long live bash

TL;DR – Bash ain’t goin’ nowhere on Mac, both version-wise and in terms of its presence. Looking at the longevity of other shells on the system, it will likely be around for a good while longer.

There’s been a lot of hand wringing and angst online about bash and zsh becoming the new default shell. Some folks feel Apple is signaling deprecation and removal and have the crushing feeling they must convert all their bash script to zsh. I think that’s a bit unnecessary.

True, the default shell is changing from bash to zsh, as Apple notes here. This is indeed a Good Thing™ as zsh shell has been one of the most frequently updated shells on macOS. Bash, on the other hand, has been stuck at varying versions of 3.2 for 12 years now! On the plus side, sysadmins have “enjoyed” predictable and stable behavior from bash during this time. Sure, you’d love new features but when you are scripting for the enterprise, across multiple OS versions, this is just the sort of thing you want: boringness and dependability.

As far as deprecations go, the only thing Apple has signaled as being deprecated eventually are scripting language runtimes (not shells):

Scripting language runtimes such as Python, Ruby, and Perl are included in macOS for compatibility with legacy software. Future versions of macOS won’t include scripting language runtimes by default, and might require you to install additional packages. If your software depends on scripting languages, it’s recommended that you bundle the runtime within the app. (49764202)

Use of Python 2.7 isn’t recommended as this version is included in macOS for compatibility with legacy software. Future versions of macOS won’t include Python 2.7. Instead, it’s recommended that you run python3 from within Terminal. (51097165)

mac OS Catalina 10.15 Release Notes

I’ve done some digging and culled the shell versions from OS X 10.0 to macOS 10.15, along with their respective release dates. I think it shows that shells, no matter how old and crusty, tend to be long lived and not soon removed on macOS.

Here’s a quick way to check your shell versions (except for dash):

macOSzshbash/shcsh/tcshkshdash
10.03.0.8 (2000-05-16)3.0.8 (zsh, no bash)6.08.00 (1998-10-02)
10.13.0.8 (2000-05-16)3.0.8 (zsh, no bash)6.10.00 (2000-11-19)
10.24.0.4 (2001-10-26)2.05b.0 (2002-07-17)6.10.00 (2000-11-19)
10.34.1.1 (2003-06-19)2.05b.0 (2002-07-17)6.12.00 (2002-07-23)
10.44.2.3 (2005-03-00)2.05b.0 (2002-07-17)6.12.00 (2002-07-23)M p (1993-12-28)
10.54.3.4 (2007-04-19)3.2.17 (2007-05-01)6.14.00 (2005-03-23)M s+ (1993-12-28)
10.64.3.4 (2008-11-03)3.2.48 (2008-11-18)6.15.00 (2007-03-03)M s+ (1993-12-28)
10.74.3.11 (2010-12-20)3.2.48 (2008-11-18)6.17.00 (2009-07-10)M s+ (1993-12-28)
10.84.3.11 (2010-12-20)3.2.48 (2008-11-18)6.17.00 (2009-07-10)JM 93u (2011-02-08)
10.95.0.2 (2012-12-12)3.2.51 (2010-03-17)6.17.00 (2009-07-10)JM 93u (2011-02-08)
10.105.0.5 (2014-01-06)3.2.57 (2014-11-07)6.17.00 (2009-07-10)AJM 93u+ (2012-08-01)
10.115.0.8 (2015-05-31)3.2.57 (2014-11-07)6.18.01 (2012-02-14)AJM 93u+ (2012-08-01)
10.125.2 (2015-12-02)3.2.57 (2014-11-07)6.18.01 (2012-02-14)AJM 93u+ (2012-08-01)
10.135.3 (2016-12-12)3.2.57 (2014-11-07)6.18.01 (2012-02-14)AJM 93u+ (2012-08-01)
10.145.3 (2016-12-12)3.2.57 (2014-11-07)6.18.01 (2012-02-14)AJM 93u+ (2012-08-01)
10.155.7.1 (2019-02-03)3.2.57 (2014-11-07)6.21.00 (2019-05-08)AJM 93u+ (2012-08-01)dash-9 (1993)

As you can see, zsh has has been updated with almost every new release of macOS. Bash really hit a wall with 3.2 and as many have noted, it was v4’s change in licensing to GPLv3 that caused this (sh is really bash in sh compatibility mode so the versions are intertwined). csh/tcsh has the same duality thing going on and took a notably giant 7 year leap in 10.15 to a version from 2019. ksh has remained at the same version just as long as bash yet I don’t think anyone is fretting that ksh will be deprecated or removed. Finally, dash is just a weirdo that apparently disdains versioning! I used a combination of what /bin/dash and man dash to get some sort of crude answer.

So there you go: In my opinion, all signs point to bash being yet another shell on macOS for some time. Removing bash from macOS would break a lot of stuff and while that reason alone hasn’t stopped Apple before, I think they will let sleeping dogs lie. Go ahead and learn zsh, master it, customize it, or make it “sexy” but take the rumors of its demise on macOS with a grain of salt and dose of skepticism.

Determining the current console user in macOS

There’s a few methods floating out on the web, but I’d just like to point out a quick and reliable way to get the current console user on macOS using stat. This works great with Fast User Switching and if the mac is at the login screen it will return root as the user

consoleUser=$(stat -f %Su /dev/console)

There you go! Now go forth and script.

Also, if you are using backticks ` to capture output (a.k.a. command substitution) then consider using the more modern and nest-able $(…) method, here’s a good reason why: Why is $(…) preferred over `…` (backticks)?

How to determine if your Mac has a T2 chip

Do you know if your MacBook Pro has a T2 chip in it?
Here’s an easy way to tell:

Goto Identify your MacBook Pro model

Then look for the the murderous autonomous robot. Easy!

You can also run this:

/usr/libexec/PlistBuddy -c "print :0:_items:0:ibridge_model_name" /dev/stdin 2>/dev/null <<< "$(/usr/sbin/system_profiler -xml SPiBridgeDataType -nospawn)"

<<< – is an über cool “here string”

PlistBuddy is great at parsing data defaults might balk at, it can also take /dev/stdin!

Nothing (bash style)

The brunerd blog has been a whole lot of nothing lately, so let’s commemorate and celebrate with an entry featuring the premiere of the bash tag and category… hmm, which one is more useful? We’ll find out. Now as for nothing, there’s a legit command, special built-in to do nothing in bash!

man page for bash command :

: [arguments]

No effect; the command does nothing beyond expanding arguments and performing any specified redirections.  A zero exit code is returned.

 

Isn’t that lovely? When you need to do nothing, you can call :
Perhaps you are writing a script and aren’t sure what you’ll do for that if statement —

if [ "${this}" == "that" ]; then
#actually not sure what to do here, let's do nothing
:
else
echo "This else, I'll do"
fi

I made a gist here and I think I’ll keep making more. I’ll feature them here with the bash tag, if only as an auxillary backup of my brain and to add to the collective patchwork quilt of code we all use to get our ideas off the ground, when our reach is just a hare shy of grasp and we need a little help.

Also in the gist was a snippet of a novel use by kbeck at Extensis in his UTC removal script to use : to redirect a here document into itself and server as a comment block that doesn’t need # on every line to be treated as a comment —

: <<COMMENTBLOCK

[ ] 2016-0324: enclose me; love me. i am a block of comments

COMMENTBLOCK

Spiffy!

RecoveryHD Updater now with PKG building

** Update: Recovery HD updaters packages for 10.11 will only work when run in OS X 10.11, previous packages could run in different OSesnot so for 10.11

Updated April 6, 2015: createRecoveryHDUpdater 15.4.6.command – built PKG now is targetable to volumes other than /

Released Feb 28, 2015: createRecoveryHDUpdater 15.2.28.command

Recovery15.2.28

Building off the previous work the updated script will now create a nicely named dmg

recoverydmg_icon

Containing both an interactive script for updating disks and a new script to create a .pkg!

recoverydmg_2scripts

 

The fruits of RecoveryHD PKG Maker.command:

recoverypkg_iconCustom Bundle ID is interactively provided, the PackageVersion is matched to the OS release. Signing identity is asked for in the script but not tested (my cert might be broken, YMMV). The target of the package is always / – an exercise for the reader to change the postinstall script to use Installer’s variables to affect the target The exercise to make the .pkg targetable has been exercised by me… Enjoy!