How to get a notification when a terminal command finishes on Mac

You start a build, switch to something else, and check back five minutes later. It finished four minutes ago. Or it failed after ten seconds. Here are five ways to have the terminal tell you instead, from a one-off command to automatic alerts on your phone.

1. Chain a notification onto the command

macOS can show a notification from the command line with osascript, which ships with every Mac. Put it after your command with ; so it fires whether the command succeeds or fails:

npm run build; osascript -e 'display notification "Build finished" with title "Terminal"'

Use && instead of ; if you only want to hear about success. Better still, include the exit code so the notification tells you whether it worked:

npm test; code=$?; osascript -e "display notification \"Exit code $code\" with title \"Tests finished\""

On recent versions of macOS, these notifications appear under Script Editor. If nothing shows up, open System Settings → Notifications → Script Editor and allow notifications.

Or just make a sound

If you're in the same room, a sound is enough:

make release; say "release finished"

say speaks any text, and afplay /System/Library/Sounds/Glass.aiff plays a system sound.

2. Use the terminal bell

Every terminal understands the bell character. Printing it is the oldest way to ask for attention:

./long-script.sh; printf '\a'

What the bell does depends on your terminal app. In Terminal.app, the profile settings (Settings → Profiles → Advanced) control whether it plays a sound, flashes the window or badges the Dock icon. iTerm2 can turn a bell into a Notification Center alert in its profile settings. Many command-line tools, including test runners and coding agents, ring the bell themselves when they finish or need an answer.

3. Get notified about every slow command automatically

Remembering to add ; say done is the hard part. zsh, the default shell on macOS, can do it for you: record when a command starts, and when it ends, notify you if it took longer than a threshold. Add this to ~/.zshrc:

# Notify when a command takes longer than 30 seconds.
_notify_start() { _cmd_start=$SECONDS; _cmd_name=${1[1,40]} }
_notify_done() {
  local code=$?
  if [[ -n $_cmd_start ]] && (( SECONDS - _cmd_start > 30 )); then
    osascript -e "display notification \"Exit $code after $((SECONDS - _cmd_start))s\" with title \"Command finished\""
  fi
  unset _cmd_start
}
autoload -Uz add-zsh-hook
add-zsh-hook preexec _notify_start
add-zsh-hook precmd _notify_done

preexec runs just before a command starts and precmd just before the next prompt appears, so the difference is how long the command took. Note that the exit code has to be read first, before anything else in precmd changes it. (Don't call the variable status: in zsh that name is reserved.) Open a new terminal window for it to take effect.

A notification for every slow command is useful at your desk. It doesn't help once you've walked away from the Mac, which is when you need it most.

4. Send it to your phone

To hear about a job when you're not at your Mac, the notification has to leave the Mac. Two common ways:

Both tell you a job finished. Neither lets you do anything about it from your phone. If the build failed, you still have to get back to the Mac to see why.

5. Finished isn't the only thing worth knowing

Many jobs don't fail or finish. They stop and wait: Deploy to production? (y/n), a password prompt, a coding agent asking before it changes a file. Nothing in the tricks above fires, because the command is still running.

That's the case PandaSend is built for. Your terminal sessions run in PandaSend on your Mac, and PandaSend watches each one for a prompt that's been waiting a few seconds, and for the bell or notify codes a tool sends. When it spots one, your phone gets a notification naming the session. Tap it and you're in that same running session, so you can answer the question, read the error or press Ctrl-C. The notification never includes what's on screen, and everything between your phone and your Mac is end-to-end encrypted.

It works with any tool, because it watches the terminal rather than hooking into one program. There's more on this in running coding agents and long jobs from your phone.

Which one should you use?