Skip to content

Instantly share code, notes, and snippets.

@colbyn
Created November 15, 2025 17:12
Show Gist options
  • Select an option

  • Save colbyn/2e8f6798f009f48a9114fddf2ff51096 to your computer and use it in GitHub Desktop.

Select an option

Save colbyn/2e8f6798f009f48a9114fddf2ff51096 to your computer and use it in GitHub Desktop.
Open URL Lists Sequentially in Safari

URL Sequential Opener Scripts

This directory contains two Bash scripts for opening lists of URLs in Safari, one at a time, waiting for each window to be closed before proceeding to the next.

These tools are designed for workflows where you must manually inspect many pages without overloading history, CPU, or open tabs.


Scripts

open-urls-sequential.sh

Opens each URL in a normal Safari window.

open-urls-sequential-private.sh

Opens each URL in a Safari Private Window using UI scripting.

Both scripts behave identically except for window mode.


Features

  • Accepts a file path of URLs or reads from stdin.
  • Ignores blank lines and comment lines beginning with #.
  • Opens one URL per window.
  • Waits until the user closes that window before opening the next one.
  • Safe, predictable, and works with arbitrarily long URL lists.
  • Fully compatible with Safari on macOS.

Input Examples

From a file

./open-urls-sequential.sh urls.txt
./open-urls-sequential-private.sh urls.txt

From stdin

cat urls.txt | ./open-urls-sequential.sh

Inline

printf '%s\n' \
  "https://example.com" \
  "https://apple.com" \
  | ./open-urls-sequential.sh

URL List Rules

A valid list looks like:

# new leads
https://example.com
https://doc.rust-lang.org/cargo/index.html

# skip this section
# https://news.ycombinator.com

https://graphemica.com
  • Blank lines = skipped
  • Lines starting with # = skipped
  • Everything else must be a URL

Requirements

  • macOS
  • Safari

Requirements for open-urls-sequential-private.sh

  • Terminal/iTerm allowed under: System Preferences → Security & Privacy → Accessibility (Required for “New Private Window” UI automation)

Intent

These scripts are built for workflows where:

  • You need to process long sequences of URLs.
  • You want windows to open one at a time, not all at once.
  • You want Safari windows to auto-block navigation until you close them.
  • You prefer private-history browsing when reviewing many pages.

They are straightforward, robust, and tuned for high-volume sequential browsing.

#!/usr/bin/env bash
set -euo pipefail
# ------------------------------------------------------------------------------
# open-urls-sequential-private.sh
#
# Read a list of URLs (one per line) and open them sequentially in Safari
# Private Windows. Each URL gets its own private window, and the script waits
# until you close that window before opening the next one.
#
# Input:
# - If a file path is passed as the first argument, URLs are read from that file.
# - Otherwise, URLs are read from stdin.
#
# Behavior:
# - Blank lines and lines starting with '#' are ignored.
# - For each URL:
# * A new Safari Private Window is opened via UI scripting.
# * The URL is loaded into that private window.
# * The script waits until that window is closed before continuing.
#
# Requirements:
# - macOS
# - Safari installed
# - "System Events" automation allowed for Terminal/iTerm (Accessibility perms)
#
# Examples:
# ./open-urls-sequential-private.sh links.txt
# cat links.txt | ./open-urls-sequential-private.sh
# ------------------------------------------------------------------------------
# Decide where to read URLs from: file arg or stdin
if [[ $# -gt 0 ]]; then
input="$1"
if [[ ! -f "$input" ]]; then
echo "Error: input file not found: $input" >&2
exit 1
fi
exec < "$input"
fi
while IFS= read -r url || [[ -n "${url:-}" ]]; do
# Trim leading/trailing whitespace
url="${url#"${url%%[![:space:]]*}"}"
url="${url%"${url##*[![:space:]]}"}"
# Skip empty lines and comments
[[ -z "$url" || "${url:0:1}" == "#" ]] && continue
echo "Opening (private window): $url"
/usr/bin/osascript <<EOF
set theURL to "$url"
tell application "Safari"
activate
end tell
-- Open a Private Window via UI scripting
tell application "System Events"
tell process "Safari"
click menu item "New Private Window" of menu "File" of menu bar 1
end tell
end tell
delay 0.3 -- let the private window appear
tell application "Safari"
-- Load the URL in the frontmost (new) private window
set front document's URL to theURL
-- Track that window
set wid to id of front window
end tell
-- Wait for that specific private window to close
repeat
tell application "Safari"
set winIDs to id of every window
end tell
if winIDs does not contain wid then exit repeat
delay 0.25
end repeat
EOF
done
#!/usr/bin/env bash
set -euo pipefail
# ------------------------------------------------------------------------------
# open-urls-sequential.sh
#
# Open a list of URLs in Safari, one at a time, in normal (non-private) windows.
#
# Input:
# - If a file path is passed as the first argument, URLs are read from that file.
# - Otherwise, URLs are read from stdin.
#
# Rules:
# - Lines starting with '#' are treated as comments and ignored.
# - Blank / whitespace-only lines are ignored.
# - For each URL:
# * A new Safari window is created and navigated to the URL.
# * The script waits for that window to be closed before proceeding.
#
# Examples:
# ./open-urls-sequential.sh urls.txt
# cat urls.txt | ./open-urls-sequential.sh
# printf '%s\n' "https://example.com" "https://apple.com" | ./open-urls-sequential.sh
# ------------------------------------------------------------------------------
# If a file argument is provided, read from that file; otherwise use stdin.
if [[ $# -gt 0 ]]; then
input="$1"
if [[ ! -f "$input" ]]; then
echo "Error: input file not found: $input" >&2
exit 1
fi
exec < "$input"
fi
while IFS= read -r url || [[ -n "${url:-}" ]]; do
# Trim leading/trailing whitespace
url="${url#"${url%%[![:space:]]*}"}"
url="${url%"${url##*[![:space:]]}"}"
# Skip empty lines and comments
[[ -z "$url" || "${url:0:1}" == "#" ]] && continue
echo "Opening (normal window): $url"
/usr/bin/osascript <<EOF
set theURL to "$url"
tell application "Safari"
activate
set w to make new document with properties {URL:theURL} -- always a new window
delay 0.3 -- let the window materialize
set wid to id of front window
end tell
-- wait ONLY for that window to disappear
repeat
tell application "Safari"
set winIDs to id of every window
end tell
if winIDs does not contain wid then exit repeat
delay 0.25
end repeat
EOF
done
@colbyn
Copy link
Author

colbyn commented Nov 15, 2025

Prefer open-urls-sequential.sh since it doesn't require accessibility permissions (although it can overload your browsing history).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment