This series is an excerpt from the first chapter of my upcoming book “Scripting macOS” which will teach you to use and create shell scripts on macOS.
- Part 1: First Script
- Part 2: The Script File
- Part 3: The Code
- Part 4: Running the Script
- Part 5: Lists of Commands
- Part 6: Turning it off and on again
- Part 7: Download and Install Firefox
I will publish one part every week. Enjoy!
Beyond Hello, World
Now that you have a minimal working script, let’s extend it a bit.
Create a copy of the
hello.sh script file, set its executable bit, and open it in your favored text editor:
> cp hello.sh hello_date.sh > chmod +x hello_date.sh > bbedit hello_date.sh
A script is a list of commands that the interpreter will process sequentially. Up to now, we only have a single command, the line with
echo, in our script. We will add another one:
Change the text in the script file:
#!/bin/zsh # Greetings echo "Hello, World!" date
We have added a line to the script with the
If you are unfamiliar with this command, you can try it out in the interactive command line:
> date Tue Feb 23 10:23:05 CET 2021
When you invoke
date with out any arguments it will print out the current date and time. The date command has many other functions for doing
date and time calculations which we will not use right now, but you can read about in the
date man page.
Save the modified script and execute it:
> ./hello_date.sh Hello, World! Tue Feb 23 10:27:06 CET 2021
As we can tell from the output, each command in the script was executed one after the other and the output of each command was shown in Terminal.
You can insert more
echo commands before the
date to make the output prettier:
#!/bin/zsh # Greetings echo "Hello, World!" # print an empty line echo # print without line break echo -n "Today is: " date
As we have learned earlier, empty lines and lines starting with a
# character will be ignored, but can serve to explain and clarify your code. I will be using comments in the example scripts for quick explanations of new commands or options.
Here I added the
-n option to the third
echo command. This option suppresses the new line character or line break that
echo adds automatically at the end of the text. This will then result in the output of the
date command to print right after the ‘
Today is:’ label, rather than in a line of its own.
Take a look:
> ./hello_date.sh Hello, World! Today is: Tue Feb 23 10:30:44 CET 2021
date command allows for changing the format of the date and time output. It uses the same formatting tokens as the C
strftime (string format time) function. You can read the
strftime man page for details. The
%F format will print the output as ‘year-month-date:’
> date +%F 2021-02-23
You can combine place holders:
> date +"%A, %F" Tuesday, 2021-02-23
You can experiment with the date formatting placeholders from the
strftime man page in the interactive shell. Once you have built a formatter that you like, you can add it to the date command in your script.
#!/bin/zsh # Greetings echo "Hello, World!" # print an empty line echo # print without line break echo -n "Today is: " date +"%A, %B %d"
It is very common that you will test and iterate a command and arguments in the interactive shell before you add or insert it into your script. This can be a much easier and safer means of testing variations of commands and options than changing and saving the entire script and running it repeatedly.
Scripts are basically ‘lists of commands.’ Once you know the steps to perform a workflow in the interactive terminal, you can start building a script. Let’s look at another example.
Desktop Picture Installer
When you copy an image file to
/Library/Desktop Pictures, it will appear in the list of pictures in the ‘Desktop & Screen Saver’ pane in System Preferences. On macOS 10.15 Catalina and higher you may have to create that directory.
You can easily build an installer package (pkg file) that installs an image file into that location with the
First, create a directory to hold all the sub-directories files we will need. There needs to be a payload directory in the project directory. Copy the image file into the payload directory:
> mkdir BoringDesktop > cd BoringDesktop > mkdir payload > cp /path/to/BoringBlueDesktop.png payload
You can then build an installer package with the following command:
> pkgbuild --root payload --install-location "/Library/Desktop Pictures/" --identifier blog.scripting.BoringBlueDesktop --version 1.0 BoringDesktop-1.0.pkg pkgbuild: Inferring bundle components from contents of payload pkgbuild: Wrote package to BoringDesktop-1.0.pkg
This will create a file
BoringDesktop-1.0.pkg. When you double-click this file, it will open the Installer application and, when you proceed with the installation, put the image file in
Note: To learn more about using and building installer package files for macOS, read my book “Packaging for Apple Administrators.”
pkgbuild command has a lot of arguments and when you get any of them just slightly wrong, it may affect how the installer package works. This is a very simple example, but when you build more complex installer packages, you may be building and re-building many times and you want to avoid errors due to typos.
We can copy this big command and place it in a script file:
#!/bin/zsh pkgbuild --root payload --install-location "/Library/Desktop Pictures/" --identifier blog.scripting.BoringBlueDesktop --version 1.0 BoringDesktop-1.0.pkg
Then make the script file executable:
> chmod +x buildBoringDesktopPkg.sh
Now you can just run the script and not worry about getting the arguments ‘just right.’
But we can go one step further and make the script more readable. We can add a comment describing what the script does.
The shell usually assumes the line break to be the end of the command. But when you place a single backslash
\ as the last character in a line, the shell will continue reading the command in the next line. That way, you can break this long, difficult to read command into multiple lines:
#!/bin/zsh # builds the pkg in the current directory pkgbuild --root payload \ --install-location "/Library/Desktop Pictures/" \ --identifier blog.scripting.BoringBlueDesktop \ --version 1.0 \ BoringDesktop-1.0.pkg
The arguments are now more readable. When you want to change one of the values, e.g. the version, it is easier to locate.
Even though this script only contains a single command, it improves the workflow significantly, as you do not have to remember a long complex command with many arguments.
Of course, you can copy and modify this script for other package building projects.
Next Post: Turning it off an on again