How To Run Shell Script Mac

How To Run Shell Script Mac

Reading time1 min
#Mac#Automation#Scripting#ShellScript#Terminal#Unix

Mastering the Essentials: How to Run Shell Scripts on Mac Like a Pro

System administrators and developers rely on shell scripts to automate deployments, maintenance, and data manipulation tasks. On macOS, far too many professionals overlook the command line’s built-in capabilities—often reaching for third-party apps or workflow tools when native Unix scripting suffices.

Why Scripts Over the GUI?

Repetitive keystrokes waste engineer-hours. Need nightly backups, data munging, or customized reporting? Shell scripts solve these with clarity and version control—offering direct access to every low-level system API, provided you know how to run them properly.


Shell Scripts: Nuts and Bolts

Fundamentally, a shell script is a plain text file containing shell commands, optionally structured with control flow and argument variables.

Example (example.sh):

#!/bin/bash
echo "Snapshot of running processes:"
ps aux | head -5

Note: The first line (#!/bin/bash) specifies the interpreter; bash remains common, but since macOS Catalina, /bin/zsh is default. Validate your script's portability before embedding complex syntax.


Step 1: Author the Script File

Use a reliable editor—nano, vim, or any robust code editor (Sublime, VS Code, BBEdit). Always avoid formatting issues from GUI text editors like TextEdit unless in plain text mode; hidden characters can break scripts.

nano ~/Desktop/listdirs.sh

Contents:

#!/bin/zsh
ls -lh ~/

Save and exit (Ctrl+O, Enter, Ctrl+X).


Step 2: Terminal—Your Launchpad

Access Terminal via Spotlight (Cmd + Space, type “Terminal”), or from /Applications/Utilities/Terminal.app.


Step 3: Navigate and Permissions

Change directories (cd), for instance:

cd ~/Desktop

Set executable bit:

chmod +x listdirs.sh

If omitted, you’ll see:

zsh: permission denied: ./listdirs.sh

Side note: In multi-user systems, habitually review ownership (ls -l). Scripts owned by root or different users will need sudo or permissions adjusted.


Step 4: Execute

./listdirs.sh

Or, invoke the shell explicitly (avoids needing execute permission):

zsh listdirs.sh
# or
bash listdirs.sh

This distinction matters when running scripts from cron, CI jobs, or while prototyping in a shared directory.


Passing Inputs: Parameterization

Most practical scripts need arguments:

hello.sh:

#!/bin/zsh
echo "Hello, $1. Time: $(date)"

Run with:

./hello.sh MacEngineer

Result:

Hello, MacEngineer. Time: Sat Jun 8 09:27:31 PDT 2024

Automating Execution: cron vs. launchd

ToolUse CaseGotchas
cronSimple schedules across POSIX systemsMay require full path to scripts, and environmental variables are limited.
launchdSystem-level and per-user recurring jobs on macOSProperty list (.plist) syntax is verbose; troubleshooting via launchctl list and system logs.

Example: Add with crontab -e:

0 4 * * * /Users/alex/scripts/backup.sh >> /tmp/backup.log 2>&1

For launchd, use a property list targeting your script, then load with launchctl load.


Debugging and Non-Obvious Issues

  • Script hangs or fails: Prefix script with set -euxo pipefail for verbose output and safer error handling.
  • Path confusion: Add echo $PATH at script start; sometimes the PATH is minimal under cron or launchd.
  • Shebang mismatch: If portability is key, prefer /bin/sh unless advanced Bash or Zsh features are needed.

Practical Example: Automated Cleanup

Replace old log files with a daily cleanup job:

#!/bin/bash
find /var/log/myapp/ -type f -mtime +7 -delete

But: On macOS, the default shell changed to Zsh (/bin/zsh) in 10.15 (Catalina). Scripts created before 2019 might assume Bash, so legacy scripts may fail without adaptation.


Key Takeaways (Mid-article)

  • macOS scripting is robust out-of-the-box; most workflow automation should start here.
  • File permissions are not just formalism—neglecting them is a common point of failure.
  • Environmental differences between Terminal sessions and scheduled tasks will surface. Plan to log, debug, and adapt your script context.

Known Issues & Advanced Notes

  • Some macOS security settings (Gatekeeper, quarantine attribute) can block script execution if downloaded from the web. Check with xattr if you encounter “Operation not permitted.”
  • For repeatable builds or fleet setup, include version checks at script start:
[[ $(uname) == "Darwin" ]] || { echo "Not macOS"; exit 1; }
  • Symbolic links to scripts (ln -s) in your ~/bin or /usr/local/bin directory streamline complex workflows, but remember to manage the PATH.

Summary

macOS offers a native, script-friendly Unix environment—no third-party automation required for typical engineering tasks. Shell scripts optimize repetitive work, enable tight system integration, and are fully compatible with source control and CI/CD pipelines. Understand permissions, interpreter versions, and scheduling tools. Adjust for context (interactive shell vs. cron/launchd).

Some tasks do require more elaborate orchestration (Ansible, Python, Swift scripting), but for single-machine automation, shell scripts remain engineering gold.

Questions or edge-case failures? Compare PATH and shell between interactive and automated contexts before blaming the OS.