Guide for Apple IT: Introduction to Mac Scripting

Posted on January 14, 2020

In this beginner-friendly guide, we’re going to walk you through the building blocks of Mac scripting, and then we’ll see how using scripts with your mobile device management (MDM) solution can help IT more effectively manage company owned devices.

Here’s a quick overview of some of the topics we’ll cover:

  • What are Scripts?
  • Using Scripts with an MDM Solution
  • Terminal Commands vs. Shell Scripts
  • What are Shells: Bash vs. Zsh?
  • Basics of Scripting with Bash

What are Scripts?

Scripts are small computer programs that collect a series of commands into one file. While typing commands into Apple’s Terminal is typically done one by one, with a script, you can execute a long list of commands with just one interaction.

While you typically won’t use Mac scripts to make larger programs, you can use them to automate routine computer behaviors. For instance, if you have a time-consuming routine that takes a lot of clicks, you can write up a script and do it with just one.

Apple puts it well in their Shell Scripting Primer:

Writing a shell script is like riding a bike. You fall off and scrape your knees a lot at first. With a bit more experience, you become comfortable riding them around town, but also quickly discover why most people drive cars for longer trips.

Shell scripting is generally considered to be a glue language, ideal for creating small pieces of code that connect other tools together. While shell scripts can be used for more complex tasks, they are usually not the best choice.

Learning how to script is similar to learning a programming language. You start with the basics and build up your knowledge from there. 

So, in this guide, we’re going to focus on the building blocks of scripting. The information we cover here will be useful in plenty of settings, but we’re ultimately going to focus on how scripting can help you perform actions on multiple devices using an MDM solution.

What Can Scripts Do?

Essentially, scripts let you do complicated things quickly, accurately, and easily.

  • Quickly because using Mac scripts in an MDM can automate tedious tasks, like accessing a computer program on 100 company devices – with zero clicks rather than 100.
  • Accurately because a good script will execute a defined action the same way every time – while trusting a person to complete the task manually could lead to errors, inconsistency, and confusion.
  • Easily because you can accomplish a really complicated and nuanced task with a series of simple scripts that break it down into smaller pieces.

Using Scripts with an MDM Solution

Using scripts on their own can help you save time, but using them with an MDM solution, like our product Kandji, opens up a lot more power and freedom. After you write a script you like, you can run it through your MDM solution to apply it to all of your company’s devices.

How scripts are used in MDM solutions will differ depending on the product, but with Kandji you can use them to do things like customize custom app installation to meet your company’s exact needs.

In this case, if your business wants to install a custom app across all company devices, you could specify certain preinstall or postinstall actions by adding scripts to their respective input boxes (see in the image below):

mac bash script preinstall postinstall

Preinstall Scripts

You can create a preinstall script if you want to perform an action before installation occurs. Preinstall scripts are generally used to:

  • Perform cleanup tasks that would normally be done manually before reinstalling software, such as removing licensing, caches, database files, and more.
  • Run configuration files. Many applications employ mass-deployment options that work by finding configuration files placed before the installer is run. For example, you might use a preinstall script to create a particular folder, and write a file inside that folder containing some sort of identifier. When the installation occurs, the installer finds this information and uses it to automatically configure the application. 

Postinstall Scripts

You can also create a postinstall script, which is generally used to configure application preferences before the software is run. To share a common example, you could use a posinstall script to configure user preferences for Google Chrome, and ensure that automatic updates are configured.

As you can imagine, scripting these behaviors gives you a lot more control over the installation process, and it can save your IT team a lot of time if they’d have to do it manually otherwise.

Custom Scripts

Kandji also has a Custom Script feature that lets you add any script supported by macOS. These Mac scripts can run once per device or continuously. There’s also a remediation script option that you can include when needed. You can see more of the available options on Kandji’s Custom Script page below:

mac scripting - kandji apple mdm custom script

Building Blocks of Scripting

Now that we have a working definition of what scripts are and know what they can accomplish, let’s learn how it all works. In this section, we’ll break down some of the major building blocks of Mac scripting, including Terminal commands, shell scripts, and two common shells.

What are Terminal Commands?

Terminal is the macOS command line interface (CLI). It lets users make advanced modifications to their Mac using commands, which are just instructions to your Mac regarding what you want it to do.

mac scripting terminal

Image source: support.apple.com

A word of warning, though: Terminal is an incredibly powerful tool. Thoroughly research and test commands before running them, and when possible, test with a VM instead of a production machine.

When you type commands into Terminal, it lets you communicate with an interpreter, or shell. The interpreter takes code and translates it into machine language that the OS can understand.

To open Terminal, just click on “Applications,” open the “Utilities” folder, and then double-click the “Terminal” app. You can use Terminal commands for anything that you can accomplish in the GUI – and plenty more.

Here are some basic examples of commands:

  • Add text to a document (if the document doesn't exist, a new one will be created)
echo 'Welcome to my document!' > ~/Desktop/myDocument.txt
  • Make a folder in a certain location
mkdir ~/Desktop/myFolder
  • Move a document to a certain location
mv ~/Desktop/myDocument.txt ~Desktop/myFolder
  • Open a document from a certain location
open ~/Desktop/myFolder/myDocument.txt

This is a great way to see how you can use commands to make your Mac perform certain behaviors, but it isn’t very practical if you want to perform a series of tasks. We’ll see how scripts make this possible in the next section.

What are Mac Shell Scripts?

Of course, plugging in Terminal commands one by one will become pretty tedious when you’re trying to roll out changes for hundreds of company-owned Mac devices. So, rather than execute individual commands like our example in the last section, you can turn all of your commands into one script – a shell script – that can run without direct interaction. This makes it easier to execute repeating or multi-step processes while minimizing error.

A shell script is just a text file containing UNIX commands (commands that talk to your operating system – macOS is a UNIX-based operating system). Everything you can do with Terminal commands you can do with Mac shell scripts, just a lot more easily. You can even automate shell scripts with tools like launchd.

Run Shell Script: Mac Terminal

It’s actually pretty simple to run a shell script. If we were to make a Mac script out of the Terminal commands introduced above, we’d just have to do this:

  1. Type #!/bin/bash into the first line. The first two characters, called a “shebang” (#!), let Terminal know that you’re typing a shell script. The rest of the line specifies what shell the scripts should run with. In this case, it’s bash, but you could also specify “zsh,” which is another shell we’ll also cover in the next section.
  2. Add the commands into a script editor. Just like word processors are specialized for writing, script editors are specialized for scripting, doing things like color coding words of certain functional categories and flagging errors. There are a lot of script editors out there, but two of the more popular (and free) ones are Atom and bbedit.
  3. Save it as “myscript.bash” or as any other name – just make sure that you keep the file extension (the part after the period) labeled as bash. Otherwise, Terminal won’t be able to run it properly.
  4. Authorize Terminal to execute your script. Now you just need to give Terminal permission to run your script. Don’t copy and paste the content – you need to tell Terminal to make the script file executable using the chmod +x command, for example:
    chmod +x ~/Desktop/myFolder/myscript.bash
  5. Press “enter” to run shell script. Mac’s Terminal will handle the rest!

That’s it – the program will execute the list of commands in order. It’ll probably happen faster than you can follow, but that’s just another benefit of Mac scripting – it’s really fast.

Before talking about other things you can do with scripts, let’s get an overview of what shells (like “bash,” the one we used in this example) are.

What are Shells?

When you open up Terminal, the system launches a shell program. Shells are command line interpreters which, put simply, means they take your commands and give them to the operating system to put into action. 

By default, Mac uses either zsh (Z shell) or bash (Bourne-again Shell) as the shell or command-line interpreter. Your Mac’s default shell will depend on the iteration of macOS that you’re using.

  • zsh (Mac Catalina): If you’ve created a new user account on macOS Catalina beta or later, then your default shell is zsh. macOS iterations before this use bash.
  • bash (Earlier macOS): If you’re using macOS Mojave and earlier, then bash is your default shell.

Whether your account uses a bash or zsh Mac default shell, you can change it in just a few steps. Here’s what you need to do:

  1. Click on the Apple menu
  2. Choose System Preferences > Users & Groups
  3. Click the lock and enter your account name and password.
  4. Control-click your user name in the list of users > Choose Advanced Options
  5. Look at the ”Login shell” menu and choose a shell
  6. Click OK when you found the shell you want

Bash vs. Zsh: Mac Scripting

Bash and zsh actually have a lot of features in common, and you can switch from one to the other without hitting too much of a learning curve. So, before we get into what makes them different, let’s see a few places where they overlap.

  • Z Command: The Z command lets developers navigate through their directories easily, giving them the ability to visit a recently or frequently visited directory by typing “z” followed by the directory name. This is faster than typing out the full location like we had to do when we opened up the document in the terminal command section.
  • Auto-Completion: Both shells also include a command line auto-complete feature. To use this feature, you just have to type out a command, press the “-” key, and then press tab. You’ll see a list of available options for the command, which you can go through until you find the one you want.
  • Auto-Correction: Both shells use a form of auto-correction, but bash’s feature has to be enabled first while zsh has a built-in auto-correction feature that will automatically detect small typos.
  • Color-Customization: Both zsh and bash offer a lot of color customization tools that make understanding script easier at a glance.

When it comes to differences, bash has been the default shell for macOS users up until Catalina, and it’s still the most widely used shell among IT administrators. Because bash has a huge following, there are a lot of resources out there, making it great for beginners.

Bash is part of the Bourne family of shells, a category which also includes sh, ash, zsh, and ksh. It’s loaded with more features than we can realistically list here, but some of its unique functions include looping, conditional constructs, tilde and brace expansions, and aliases.

Zsh, Mac Catalina’s default shell, on the other hand, isn’t as popular as bash, but it still has a huge following, including open source frameworks such as Oh My Zsh, and is widely praised for its functionality. Zsh offers some unique features like file globbing, loadable modules, path expansion, startup/shutdown scripts, and more. In this guide, we’re primarily going to focus on bash.

 

The Basics of Bash

Now that we understand how scripts and shells work, it’s time to go over a few tips about scripting with bash. After that, we’ll dive into the basics like assigning variables and writing conditionals. 

Word Separation

One of the first major building blocks to wrap your head around is how bash uses word separators. While looking at examples of script, you’ll see metacharacters like ; and &. These are just two of the seven characters that bash uses to separate words while scripting.

We’ll include a list of all seven below. When you see these, don’t worry. They just keep everything organized. As you’ll see later in the conditionals section, the type of syntax used depends on what you’re trying to accomplish.

  • (
  • )
  • <
  • >
  • ;
  • |

Useful Organization Commands

Bash also gives you a number of commands you can use to organize files. We covered some of these in the Terminal command section, but here are a few more:

  • mkdir (make directory) is used to create a new directory or folder.
  • mv (move) is used to move one or more files or directories to another place.
  • rm (remove) is used to delete files.
  • rmdir (remove directory) is used to delete a directory.

Viewing Commands

If you want to do more than just move files around, you can use some of these viewing commands to see the information that the files contain. Here are two of them:

  • cat (concatenate) reads the content of one or more files and displays it. This is ideal for shorter files.
  • less is another viewing command that’s great for viewing longer files because it only displays one screen of information at a time and includes useful navigation features to search for specific words or go to the next or previous page.

What are Variables?

Variables are named placeholders that refer to a certain value. When you type in the variable name, the system treats it as if you’ve typed in the value it refers to. You can name variables using numbers, letters, filenames, or other types of data.

Variables make it easier to write scripts that use specific chunks of data -- even if that data is always changing, such as usernames. Rather than write separate commands for multiple variations of usernames, you can just define a username variable and use it as a placeholder for all of the variations that users might use.

Assigning variables is pretty easy to do. You just have to specify a variable name and a value, like so:

Variable_Name=value

Capitalization doesn’t matter here. The variable name and value can be just about whatever you want, such as:

x=1

var1=hello

var2=GOODBYE

That said, there are some restrictions about what words or characters you can use for variables (which we’ll cover next). But first, it’s important to note that bash does not use spaces before or after the equal sign while assigning variables.

That means you can’t assign a variable with spaces, like this:

Incorrect:

x = 1

You have to assign it without spaces, like this:

Correct:

x=1

However, you can use spaces in variables names by using quotes, like this:

Correct:

x="Number one"

We should also mention that you can use a Mac script to tell Terminal to return the value of a variable to you. You can do this in two ways. In both examples, “x” is the variable value that you’re looking for:

$x
${x}

You can also make a variable refer to the result of a command, such as:

variable1=$(Some Bash Command)

 

Variable Restrictions

So, what are those variable restrictions we just mentioned? Bash has twenty “reserved words” that you can’t use for variables because they’re already assigned to other important functions within the shell. Whenever you’re assigning variables, you just have to avoid these words and you won’t run into any problems with your script.

Here they are:

  • ! and time
  • [[ and ]]
  • { and }
  • if, then, elif, else, and fi
  • case and esac
  • select and in
  • while, until, for, do, and done
  • function

 

Single vs. Double Quotes

'

Text encapsulated in single quotes means it contains strictly that text as read – it won't give meaning to any special characters, such as variables or variable restrictions.

"

Double quotes preserves the literal value of some characters, but it can contain some variables or characters with special meaning in them, including:

$
'
\
!

The meaning of the character ! within double quotes is preserved only when history expansion is enabled, and \ must be followed by another special character (or a new line).

What are Conditionals?

The next big step in learning Mac scripting is figuring out how to use conditionals. Essentially, conditionals are if/then statements, which set conditions and then see if they’re being met (i.e. IF it’s 6am, THEN say “Good morning”).

In bash, the syntax of if/then statements looks like this:

if <condition>; then

<command(s)>

fi

Let’s break that down.

  1. Start with “if” and immediately follow it with its conditions.
  2. Use a semicolon to separate the “IF” condition and begin the “THEN” command. This command will only take place if the “IF” condition is met.
  3. Finally, close the if/then statement with “fi” – this is just “if” spelled backward.

Here’s an example:

if [ 5 -ge 4 ]; then

echo "That number is greater than or equal to 4"

fi

Here, we’re checking to see if a number, 5, is greater than or equal to (that’s the “-ge”) another number, 4. If it is, the “then” command will be executed, prompting us with a message: “That number is greater than or equal to 4.” Imagine how changing out the 5 and using a variable instead could improve the functionality of this script.

Once you get a hold of these statements, you can learn how to use “else” to give your commands an alternative action in the event that the “if” conditions aren’t met. For more complicated commands, you can even use “elif” (else if) to add conditions to “else” without tediously rewriting more “else” conditions.

 

Scripting with MDM Solutions

Just like using Mac scripts can make individual terminal commands more powerful, using scripts with your MDM solution can bring out your fullest management potential.

Whether you’re using our Custom Script feature or specifying pre- and postinstall behaviors for custom apps, scripting with an MDM can let you accomplish plenty of advanced device management actions.

Hopefully you’ve learned that you can use scripts to complete multi-step tasks faster and more accurately – and that you can script advanced policies and send them to all of your company’s devices using an MDM solution, like Kandji.

Beyond putting your scripts into action, Kandji has plenty of other great features like one-click compliance with major security benchmarks and zero-touch deployment. It’s everything your business needs to manage your fleet from deployment to retirement.

Get started with Kandji today.