Skip to content

Instantly share code, notes, and snippets.

@talkingmoose
Created July 15, 2025 02:48
Show Gist options
  • Save talkingmoose/aea8e1a4ec8728f68e51fbf42c7eeac1 to your computer and use it in GitHub Desktop.
Save talkingmoose/aea8e1a4ec8728f68e51fbf42c7eeac1 to your computer and use it in GitHub Desktop.
MacAdmins Conference at Penn State workshop resources — July 15, 2025

Greetings, MacAdmins campers! 🏕️

Thanks for coming today!

You’ll find everything you need in these workshop resources. Keep this page handy! *

  1. Welcome!
  2. What you’ll need for today
  3. Challenges
  4. Date format strings
  5. Commands and snippets
  6. About

Date and time

Today’s workshop is divided into two morning and two afternoon sessions:

Tuesday, July 15, 2025 🗓️ 9:00 a.m. - 4:30 p.m. EDT
Session 1 (morning) 9:00 a.m. - 10:15 a.m.
Break 10:15 a.m. - 10:45 a.m.
Session 2 (morning) 10:45 a.m. - Noon
Lunch Noon - 1:30 p.m.
Session 3 (afternoon) 1:30 p.m. - 2:45 p.m.
Break 2:45 p.m. - 3:15 p.m.
Session 4 (afternoon) 3:15 p.m. - 4:30 p.m.

Google Docs community notes

Every MacAdmins Conference workshop and presentation has a unique Google Docs community notes page for sharing notes with each other. You’re encouraged to contribute what you can.

https://docs.google.com/document/d/1G3qoznZsg39P_SwVyepFg6A1es2m18RQkcie9XGeTA0/edit?tab=t.0

#psumac-deans-2 MacAdmins Slack channel

We’re meeting at the Penn Stater Hotel and Conference Center in Deans Hall 2. There’s a channel for that!

You can chat with each other in real time. (If you’re not part of the MacAdmins Slack community, you’re missing the best support resource for anyone who manages Apple computers and devices!)

Missed a step but too shy to raise your hand? Post a quick message in the channel. Someone will probably help you catch up.

Join the Deans Hall 2 channel on MacAdmins Slack

Feedback

You can provide me feedback on this workshop here: https://bit.ly/psumac25-846567

* The clown means you’re having fun.

Here’s what you'll need for today.

We’ll be exploring these tools. Below are descriptions and links for download.

Your computer

I’ve prepared workshop materials on macOS Sequoia 15.5, but if you’re running something a little older, that should be just fine. The basics of most command line tools don’t change that much over time, and what works for me on Sequoia should probably work fine on macOS Sonoma 14.0, Ventura 13.0, or even Monterey 12.0.

If you’re livin’ la vida loca and running macOS 26 Tahoe on your production computer, that will probably work as well, but I haven’t tested!

We shouldn’t be doing anything that will alter your computer, but if you feel more secure running all the workshop materials on a virtual machine, that should be no problem. Be sure you have it ready to go first thing.

BBEdit

This is one of my favorite tools for scripting. It’s very lightweight (only a 27 MB download) from the folks at Bare Bones Software and has been around since 1992! It’s a plain text editor with some punch. It’s free to use for all the basic functions we’ll be running. If you like it and want to support its development, I encourage you to become a paying customer.

Download BBEdit It (still) doesn’t suck.

CodeRunner

CodeRunner is another great tool for writing scripts. It’s a wee bit more to download at just over 300 MB. The developer offers two ways to download — via the Mac App Store or directly from their website. I recommend downloading directly from the website, but the Mac App Store version will work just fine for everything we’ll be doing.

Keep in mind you'll be using it as a 14-day free trial if you don't purchase. Download but don’t launch it if you'll be using it in trial mode. Again, I encourage you to become a paying customer, if you like the app.

Download CodeRunner from the Mac App Store

Download CodeRunner from the website

SnippetsLab

I've used SnippetsLab for 10 years and it’s my go-to app for collecting script snippets I want to keep and not solve again or rewrite. It’s also great for sharing scripts and snippets on GitHub, which we'll talk about during the workshop.

Recently, the developer stopped charging for SnippetsLab and made it free! But it’s only available as a download from the Mac App Store. If you’re not allowed to make free purchases on your computer from the app store, don’t worry. You can still use something like BBEdit to curate your list of snippets. It’ll just be a little more difficult to organize as efficiently as SnippetsLab.

Download SnippetsLab from the Mac App Store

AirPods or headphones

If you’re someone who finds concentration difficult with background noise, you may want to bring a set of AirPods or headphones to help you tune out sound.

Challenge 1

Deconstruct the following command:

date -v1d -v+1m -v-1d -v-Friday "+%B %d"

Challenge 2

Use the date command to answer the question:

"What day of the week were you born?" (Sunday, Monday, Tuesday, etc.)

A tip for using Terminal: Press the "up arrow" key to cycle through earlier commands you just typed. Keep pressing "up arrow" to return earlier commands. Press "down arrow" to cycle through later commands.

Challenge 3

Use the date command to solve the following scenario:

Microsoft releases monthly updates to Office for Mac.

It releaes updates 16 days after the last Sunday of the prior month.

What date will Microsoft release updates in October 2025?

Challenge 4

Below are three short scripts. I suggest you copy these to your snippets library.

  • Deconstruct each script
  • Deconstruct new commands
  • Add comments above each line and explain what it's doing
#!/bin/zsh

echo -n "What is your full name? "
read userFullName

echo "Hello, $userFullName."

#!/bin/zsh

firstName="Danny R."
lastName="Swopes"

echo "$firstName $lastName"

echo "$lastName, $firstName"

#!/bin/zsh

userFullName="Danny R. Swopes"

echo "$userFullName"

id -F
id -u -n

userFullName=$( id -F )

echo "Username for $userFullName is $( id -u -n )"
echo "My computer name is $( hostname )"
echo "My computer's macOS version is $( sw_vers -ProductVersion )"
echo "My computer's serial number is $( system_profiler SPHardwareDataType | awk -F": " /"Serial Number"/'{ print $2 }' )"

Challenge 5

Copy and paste the first four lines of the code under the first comment below into Terminal. This creates four variables and assigns each a color.

Do the following:

  1. Run each test in the double square brackets in Terminal one at a time.
  2. Evaluate whether you think a test will return "0" for "true" or "1" for "false".
  3. Run "echo $?" immediately after each test to verify whether you were correct.

What conclusions can you make based on these tests?

# assign colors to variables

color1="blue"
color2="red"
color3="green"
color4="blue"

# tests

[[ green = "green" ]]

[[ green = "red" ]]

[[ $color1 = "red" ]]

[[ $color2 = Red ]]

[[ $color2 = red ]]

[[ $color2 = "red" ]]

[[ $color2 = $color3 ]]

[[ "$color1" = "$color4" ]]

[[ green ball = green ball ]]

[[ "blue ball" = "$color4 ball" ]]

Challenge 6

Create an if-then-else statement from the test below.

If the statement is true, display "True". If the statement is false, display "False". Copy and paste your entire statement into Terminal to run it.

After testing, press up arrow and re-run your statement. Does anything change?

test $(( number++ )) = 0

Challenge 7

The following snippet doesn’t work as expected.

Copy and paste the snippet in Terminal and press return to run.

It should start counting down from 10 and blast off at 0.

Fix the snippet.

for aNumber in {10..1};
do
	sleep 1
	(( aNumber-- ))
	echo $aNumber
done

echo Blast off!

Challenge 8

Deconstruct this snippet and explain what it does.

while [[ $( pgrep "TextEdit" ) != "" ]];
do
	echo "TextEdit is still running."
	sleep 1
done

echo "TextEdit is not running."

Challenge 9

Write a script that recites "99 Bottles of Beer on the Wall".

It must:

  1. Erase any text from other projects in Terminal.
  2. Ask how many bottles are on the wall.
  3. Display and speak each line of each stanza as it counts down.
  4. Take into consideration there will be only 1 bottle of beer on the wall (singular).
  5. End with "No more bottles of beer on the wall".

Below is an sample of the output it should produce.

`How many bottles of beer are on the wall? 5

5 bottles of beer on the wall.
5 bottles of beer.
Take one down. Pass it around.
4 bottles of beer on the wall.

4 bottles of beer on the wall.
4 bottles of beer.
Take one down. Pass it around.
3 bottles of beer on the wall.

3 bottles of beer on the wall.
3 bottles of beer.
Take one down. Pass it around.
2 bottles of beer on the wall.

2 bottles of beer on the wall.
2 bottles of beer.
Take one down. Pass it around.
1 bottle of beer on the wall.

1 bottle of beer on the wall.
1 bottle of beer.
Take one down. Pass it around.
No more bottles of beer on the wall.

% talkingmoose@MooseBook Pro II`

Example date: Tue Jul 15 09:00:00 CDT 2025

$ date "+%B %d, %Y"
July 15, 2024

The " + " symbol indicates the date command should format the date and time according to the format string provided.

String Behavior Example (U.S.)
%A national representation of the full weekday name Tuesday
%a national representation of the abbreviated weekday name Tue
%B national representation of the full month name July
%b national representation of the abbreviated month name Jul
%C (year / 100) as decimal number; single digits are preceded by a zero 20
%c national representation of time and date Tue Jul 15 09:00:00 2025
%D equivalent to “%m/%d/%y 07/15/25
%d the day of the month as a decimal number (01-31) 15
%E* %O* POSIX locale extensions. The sequences %Ec %EC %Ex %EX %Ey %EY %Od %Oe %OH %OI %Om %OM %OS %Ou %OU %OV %Ow %OW %Oy are supposed to provide alternate representations
%e the day of the month as a decimal number (1-31); single digits are preceded by a blank 15
%G a year as a decimal number with century. This year is the one that contains the greater part of the week (Monday as the first day of the week) 2025
%g the same year as in “%G”, but as a decimal number without century (00-99) 25
%H the hour (24-hour clock) as a decimal number (00-23) 09
%h the same as %b Jul
%I the hour (12-hour clock) as a decimal number (01-12) 09
%j the day of the year as a decimal number (001-366) 196
%k the hour (24-hour clock) as a decimal number (0-23); single digits are preceded by a blank _9
%l the hour (12-hour clock) as a decimal number (1-12); single digits are preceded by a blank _9
%M the minute as a decimal number (00-59) 00
%m the month as a decimal number (01-12) 07
%n a newline
%O the same as %E*
%p national representation of either “ante meridiem” (a.m.) or “post meridiem” (p.m.) as appropriate AM
%R equivalent to “%H:%M 09:00
%r equivalent to “%I:%M:%S %p 09:00:00 AM
%S the second as a decimal number (00-60) 00
%s the number of seconds since the Epoch, UTC (see mktime(3)) 1752588000
%T equivalent to “%H:%M:%S 09:00:00
%t a tab
%U the week number of the year (Sunday as the first day of the week) as a decimal number (00-53) 28
%u the weekday (Monday as the first day of the week) as a decimal number (1-7) 2
%V the week number of the year (Monday as the first day of the week) as a decimal number (01-53). If the week containing January 1 has four or more days in the new year, then it is week 1; otherwise it is the last week of the previous year, and the next week is week 1 29
%v equivalent to “%e-%b-%Y 15-Jul-202
%W the week number of the year (Monday as the first day of the week) as a decimal number (00-53) 28
%w the weekday (Sunday as the first day of the week) as a decimal number (0-6) 2
%X national representation of the time 09:00:00
%x national representation of the date 07/15/2025
%Y the year with century as a decimal number 2025
%y the year without century as a decimal number (00-99) 25
%Z the time zone name CDT
%z the time zone offset from UTC; a leading plus sign stands for east of UTC, a minus sign for west of UTC, hours and minutes follow with two digits each and no delimiter between them (common form for RFC 822 date headers) -0500
%+ national representation of the date and time (the format is similar to that produced by date(1)) Tue Jul 15 09:00:00 CDT 2025

comparison operators

compare two values (comparing strings using < or > is a comparison of ASCII value)

String operators		Number operators
=						-eq
!=						-ne
>						> or -gt
<						< or -lt
# use string operators when comparing alphanumerical values

[[ "$color" = "red" ]]
[[ "$color" != "red" ]]

[[ a = b ]] # false (comparing ASCII 97 = ASCII 98)
[[ a = a ]] # true  (comparing ASCII 97 = ASCII 97)
[[ a > b ]] # false (comparing ASCII 97 > ASCII 98)
[[ a < b ]] # true  (comparing ASCII 97 < ASCII 98)
# use number operators when comparing numerical values

[[ "$number" -eq 1 ]]
[[ "$number" -nq 1 ]]

date

display or set date and time

$ date
Tue Jul 15 09:36:41 EDT 2025
$ date -v1d -v+1m -v-1d -v-Friday "+%B %d"
July 25

echo

write arguments to the standard output

$ echo Hello world!
Hello world!

$ echo -n Hello world!

for loop

iterate over a list and run commands for each list item

for anAnimal in dog cat zebra cow pig;
do
	(( count++ ))
	echo $anAnimal
	sleep 1
done
for aNumber in {10..1};
do
	echo $aNumber
	(( aNumber-- ))
	sleep 1
done

echo Blast off!

hostname

set or print name of current host system

$ hostname
Moosebook Pro II

id

return user identity

$ id
uid=501(talkingmoose) gid=20(staff) groups=20(staff),12(everyone),61(localaccounts),79(_appserverusr),80(admin),81(_appserveradm),98(_lpadmin),701(com.apple.sharepoint.group.1),703(com.apple.sharepoint.group.3),704(com.apple.sharepoint.group.4),702(com.apple.sharepoint.group.2),33(_appstore),100(_lpoperator),204(_developer),250(_analyticsusers),395(com.apple.access_ftp),398(com.apple.access_screensharing),399(com.apple.access_ssh),400(com.apple.access_remote_ae)
$ id -F
William Smith
$ id -u -n
talkingmoose

if-then-else

evaluate a test and execute additional commands based on whether the test succeeds or fails (also see test)

if [[ a = b ]];
then
	echo "true"
else
	echo "false"
fi

math

evaluate a test and execute additional commands based on whether the test succeeds or fails (also see test)

$ echo $(( 2 + 2 ))
4

$ echo $(( 2 - 2 ))
0

$ echo $(( 9 * 8 * 7 + 65 + 432 - 1 ))
1000
$ thisNumber=1
$ thatNumber=4
$ echo $(( thisNumber + thatNumber ))
5
$ date "+%B %d"
July 15

$ date "+%s"
1752606000

$ start=$( date "+%s" )
$ echo $start
1752606000

$ stop=$( date "+%s" )
$ echo $stop
1752606300

$ echo $(( stop - start )) seconds
300 seconds
$ echo $(( number++ ))
0
$ echo $(( number++ ))
1
$ echo $(( number++ ))
2
$ echo $(( number++ ))
3
$ echo $(( number++ ))
4
$ echo $(( number++ ))
5

For a nerdy deep dive on whether you should use single or double square brackets, read Armin Briegel’s Single Brackets vs Double Brackets on his website Scripting OS X.

read

request response string

$ read myName
Bill
$ echo Hello $myName
Hello Bill

sleep

suspend execution for an interval of time (in seconds)

for anAnimal in dog cat zebra cow pig;
do
	(( count++ ))
	echo $anAnimal
	sleep 1
done

sw_vers

print macOS system version information

$ sw_vers
ProductName:		macOS
ProductVersion:		15.5
BuildVersion:		24F74
$ sw_vers -ProductVersion
15.5

system_profiler

reports system hardware and software configuration

$ system_profiler SPHardwareDataType
    Hardware Overview:

      Model Name: MacBook Pro
      Model Identifier: MacBookPro18,3
      Model Number: MKGP3LL/A
      Chip: Apple M1 Pro
      Total Number of Cores: 8 (6 performance and 2 efficiency)
      Memory: 16 GB
      System Firmware Version: 11881.121.1
      OS Loader Version: 11881.121.1
      Serial Number (system): JP24Z0XNRY
      Hardware UUID: 9CC014E4-2746-5094-A77E-DB95E5E75548
      Provisioning UDID: 00006000-000E29A43361801E
      Activation Lock Status: Disabled
$ system_profiler SPHardwareDataType | awk -F": " /"Serial Number"/'{ print $2 }'
JP24Z0XNRY

test

condition evaluation utility (also see if-then-else)

$ test a = a
$ echo $?
true

while

continue testing until a condition is met

color="green"

while [[ $color != "red" ]];
do
	echo -n "What is your favorite color? "
	read color
done
floor=1

clear

while [[ $floor -ne 5 ]];
do
	echo "Current floor is $floor"
	sleep 1
	(( floor++ ))
done

echo "Ding! Floor 5: Athleisure wear"

zsh

The default macOS user shell since 2019.

A User's Guide to the Z-shell

Fonts

Throughout my slides I used fonts from the Atkinson Hyperlegible Font family from the Braille Institute of America, Inc. They are designed to improve legibility and readability for individuals with low vision with highly distinctive letters and numbers that make reading easier and more accessible.

The font family includes:

  • Atkinson Hyperlegible™
  • Atkinson Hyperlegible Next™
  • Atkinson Hyperlegible Mono™

They’re free for everyone — from personal use to all commercial applications.

Music

Vibe is important for some folks who write code, and music is a great way to define a mood or tempo. Music for scripting should be unintrusive and indistinct. I personally prefer music with either no vocals or vocals that are in a different language. Occassionally, I'll make exceptions.

I’ve found movie and TV soundtracks (especially science fiction) to have some of the best coding music. It's often instrumental not synthesized.

The Martian (Original Motion Picture Score)

Harry Gregson-Williams


Alegría

Cirque du Soleil


Donnie Darko (Original Motion Picture Soundtrack)

Michael Andrews


Mars (Original Series Soundtrack)

Nick Cave & Warren Ellis


2Cellos

Luka Sulic & Stjepan Hauser


Twin Peaks (Soundtrack From)

Angelo Badalamenti


Westworld: Season 1 (Music from the HBO Series)

Ramin Djawadi


Westworld: Season 2 (Music from the HBO Series)

Ramin Djawadi

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment