Books

Presentations

Privacy & Cookies: This site uses cookies. By continuing to use this website, you agree to their use.
To find out more, including how to control cookies, see here:
Cookie Policy

User Interaction from bash Scripts

As MacAdmins our goal is to automate workflows when ever possible. The advantages of automation are great. You immediately reduce the workload, but also reduce the potential for someone to make a mistake, which would mean even more work later.

In nearly all cases, we want the automation to happen “magically” in the background, without any user interaction. User interaction slows down the process as the script is waiting for the user to confirm or enter data. User input also requires validating and checking user entered data.

In most cases, when you believe you need to prompt the user for, well, anything, you should take that moment to re-think your workflow. Maybe you can come up with a different workflow that can provide the data from a source that can be automated without user interaction.

If, however, you really are convinced that user interaction is necessary, then you need be aware there are many potential pitfalls in writing shell scripts with user interaction.

macOS Mojave will introduce even more pitfalls with its increased security features.

Do you really need UI?

A common example for user interaction is to get and set the Computer Name or Asset Tag/ID/Number from manual input.

This is a task that can be fully automated in many situations. As an admin you could provide a text file mapping serial numbers to a name and/or asset tag on a server, that a script can download (curl) and parse. If you have a management system or asset database, there is probably an XML or JSON API, you can use to retrieve this information from a script. You could read or scan the serial numbers from the labels of the Mac’s boxes or even get the list with the purchase order from most vendors. With that data you can pre-fill your text files or databases.

However, in some cases, especially DEP workflows it may be difficult or impossible to predict which computer will end up on which desk, especially with zero-touch deployment workflows, where a device can be sent to a user directly.

In this case, you have to question whether you need a unique, specific computer name or asset tag. Maybe the data which your management system already gathers, such as the user who enrolled the device and its serial number will (have to) be sufficient?

In many cases, however, the reasons to prompt for and set a computer name will not be technical but stem from other, external factors that the IT department may or may not have influence on.

AppleScript’s Moment of Glory

bash was built to run in a text based terminal on many different operating systems. It has (and should have) no concept of a graphical user interface. bash alone is not useful to interact with the user, unless you want to open a terminal window.

However, AppleScript does have some (basic) commands to present dialogs and notifications to the user. We can call AppleScript command from the shell with the osascript command (OSA = open scripting architecture, the underlying framework that AppleScript uses)

There are other tools that allow to display user interface from a shell script. However, they all require an additional installation. AppleScript/osascript is simpler and built-in to the OS, so it is my first choice. That doesn’t mean it is always the appropriate choice, though.

You can go to Terminal and make a dialog appear with

$ osascript -e 'display dialog "Hello from bash!"'

The display dialog AppleScript command is documented in the “StandardAdditions” dictionary. You can see it when you choose ‘Open Dictionary…’ from the File Menu in Script Editor. Then select the “Scripting Additions.osax” dictionary and choose the “User Interaction” category.

This command has a lot of options that allow us to configure the dialog. You can experiment and test with these commands in the Script Editor application. For example, you can change the names of the buttons:

When you use variable substitution, you have to use double quotes for the command strings. And then you have to escape any additional double quotes in the AppleScript command. With more complex commands and arguments this will get unwieldy very quickly.

You start a here document with the <<- characters followed by a limit string of your choice (I chose EndOfScript). Then all the text until the limit string is repeated will be fed into the osascript command.

Bash variables will be substituted in the here document, so $title will be substituted with its contents.

Note: I prefer the <<- syntax over the << syntax with osascript. The <<- syntax will ignore leading white space in the following lines, allowing me to properly indent the AppleScript code, making the entire script more legible.

The Trouble with root

Management scripts will run with root privileges. They may also be run in situations where no user is logged in. AppleScript requires a user to be logged in (and a window server to be present) to display the alert.

In many situations all of this will ‘just work’ even when the script is executed from a root process, in others, the script will fail. It is generally safer to always check the current user and run the osascript command as the currently logged in user. You can learn about how and why to do this in this article on ‘Root and Scripting’.

This also gives your script an option to continue silently or fail when no user is logged in.

The reason for this is that it will ensure that the dialog is displayed on top of all other windows. The activate command will push the targeted process to the front. Both “System Events” and “Finder” are used frequently.

In most cases this is not necessary, as the dialog will properly display on top of all other windows, even as a standalone command. There may be some weird conditions when other applications are launched in the same time frame, though. In other cases it may be better to use display notification, anyway.

In macOS Mojave, Apple Events (AppleScript Commands) can only be sent to another application with user permission.

When you try this command in macOS Mojave, the user will be prompted to allow the Terminal application to control the System Events application

If the user denies this request, the osascript command will fail with an error:

Once a user has denied access, they will not be prompted again. They will have to go to the ‘Privacy’ tab in the ‘Security & Privacy’ pane in System Preferences, search for ‘Automation’ and allow access for Terminal.

However, management scripts will usually not be executed from Terminal, but from within the context of an installation script or management agent. It may be possible to pre-approve configurations with a UAMDM configuration profile, but it will be impossible to anticipate all contexts in which your scripts may run.

For macOS Mojave it will be better to either modify your script to not wrap the command in a tell statement. Alternatively, you can use a different solution for the UI altogether. (e.g. jamfHelper, Yo, Pashua or CocoaDialog)

All of these wrapped commands in scripts will break in macOS Mojave!

MacAdmins need to go through all their management scripts and check for AppleScript UI commands wrapped in tell statements.

When you use the standalone display dialog it will be the AppleScript process itself that displays the dialog. This requires no specific permission.

Even if your dialogs may not appear on top of all the windows, this is a preferable solution.

This will also affect scripts using osascript to control or receive data from other applications.

Summary

question and revisit every use of user interaction in your workflow

when you really have to, you can use osascript with the display AppleScript commands

to be safe, run the commands as the current user

increased macOS Mojave security will require you to verify all your scripts using osascript

Totally custom icons are not possible with AppleScript. You can make it display another app’s icon by sending the ‘display notification’ command to that app, i.e. ‘tell app “Finder” to display notification …’ however, on Mojave, this will run afoul of the privacy protection and you will have to whitelist your process to be able to send events to Finder.