I use JupyterLab daily ever since I discovered all the things I can automate using code. But this isn’t a regular part of my day, between google ads, various ad platforms, organic dashboards and crm workflows it’s not always easy for me to snap back to remembering all the things I need to do to keep my notebooks, scripts and the micro tools i create to be organized and the last thing I want is to find the 15 different final versions of a script i’ve been working on which i didn’t bother naming properly and ofcourse this can all be solved with a little discipline but it’s something that i’m cultivating but a little help can go a long way.
So while I’m doing something else, I don’t want to stop and think through cd, venv, start notebooks. I just want to open the right folder and keep moving and i don’t want to cd, venv, start notebooks navigate to the right folder and make sure my files are saved in the right place. I just decided to make a simple command to do just that and to help me jump in easily.
I didn’t want to make things complicated or type an extremely long command jlon
is easy to remember does exactly what it says “JyupterLabs On”, One command opens JupyterLab in the correct folder, activates your environment, and spins up the server automatically. asks me which folder I want to start from and opens the browser.
jlon
CommandHere is what happens, step by step:
~/Documents/projects/jl/bin/activate
so the right dependencies load every time.notebooks
, scripts
, checkers
, or cron
.nohup
and writes output to ~/.jupyterlab.log
.jupyter server list
and opens it with open
on macOS.~/.jupyterlab.pid
so jloff
can stop the exact server later.Copy the block below into your ~/.zprofile
or ~/.zshrc
. Reload your shell with source ~/.zprofile
or restart Terminal. The comments explain each step so you can adjust paths and behavior without guessing.
Prerequisites:
fzf
installed with Homebrew (brew install fzf
)~/Documents/projects/jl/bin/activate
or update the path in the script# JupyterLab quick launcher and stopper
# Author: Dan Antony
# macOS Sequoia | zsh
# Paths
export JL_LOG_FILE="$HOME/.jupyterlab.log" # last launch output
export JL_PID_FILE="$HOME/.jupyterlab.pid" # PID for clean stop
# Start JupyterLab
jupyter_lab_launch() {
# 1. Activate Python environment
local VENV_ACT="$HOME/Documents/projects/jl/bin/activate"
if [[ -f "$VENV_ACT" ]]; then
source "$VENV_ACT"
else
echo "Virtual environment not found at $VENV_ACT"
fi
# 2. Choose project folder
local base="$HOME/Documents/projects"
local folders=(notebooks scripts checkers cron)
local existing=()
local f
for f in "${folders[@]}"; do
[[ -d "$base/$f" ]] && existing+=("$f")
done
if [[ ${#existing[@]} -eq 0 ]]; then
mkdir -p "$base/notebooks"
existing=("notebooks")
fi
local selected=""
if command -v fzf >/dev/null 2>&1; then
selected="$(printf "%s\n" "${existing[@]}" | fzf --prompt='Select folder: ')"
fi
selected=${selected:-notebooks}
cd "$base/$selected" || { echo "Cannot access folder $base/$selected"; return 1; }
# 3. Find open port 8888-8899
local port=""
local p
for p in {8888..8899}; do
if ! lsof -iTCP:"$p" -sTCP:LISTEN -nP >/dev/null 2>&1; then
port="$p"
break
fi
done
if [[ -z "$port" ]]; then
echo "No open port found in 8888 to 8899"
return 1
fi
# 4. Launch JupyterLab in background
if ! command -v jupyter >/dev/null 2>&1; then
echo "jupyter is not in PATH"
return 1
fi
: > "$JL_LOG_FILE"
nohup jupyter lab --notebook-dir="$PWD" --port="$port" --no-browser >"$JL_LOG_FILE" 2>&1 &
local pid=$!
echo "$pid" > "$JL_PID_FILE"
echo "Started JupyterLab (PID $pid) in $PWD on port $port"
# 5. Find tokenized URL for this directory
local url=""
local i
for i in {1..24}; do
sleep 0.5
url="$(jupyter server list 2>/dev/null | awk -v here="$PWD" -F ' :: ' 'NF==2{u=$1;r=$2;if(r==here){print u;exit}}')"
[[ -n "$url" ]] && break
done
[[ -z "$url" ]] && url="$(jupyter server list 2>/dev/null | awk 'NR==1{print $1}')"
if [[ -n "$url" ]]; then
echo "Opening $url"
open "$url"
else
echo "Could not detect server URL. Check $JL_LOG_FILE"
fi
}
# Stop JupyterLab
jupyter_lab_stop() {
local pid=""
if [[ -f "$JL_PID_FILE" ]]; then
pid="$(cat "$JL_PID_FILE")"
fi
if [[ -n "$pid" && $(ps -p "$pid" -o pid=) ]]; then
echo "Stopping JupyterLab (PID $pid)"
kill "$pid" && sleep 1
if ps -p "$pid" >/dev/null; then
kill -9 "$pid"
fi
else
pkill -f jupyter-lab
fi
rm -f "$JL_PID_FILE"
}
# Aliases
alias jlon="jupyter_lab_launch"
alias jloff="jupyter_lab_stop"
alias jlst="jupyter server list"
alias jllog="tail -n 100 $JL_LOG_FILE"
brew install fzf
.jupyter server list
for the current URL.~/.zprofile
or ~/.zshrc
and reload your shell.jlon
. Pick a folder when prompted.jloff
.$ jlon
Select folder: scripts
Started JupyterLab (PID 19321) on port 8888
Opening: http://localhost:8888/?token=bb7e123abcd...
$ jloff
Stopping JupyterLab (PID 19321)...
Every time you use it, Jupyter opens exactly where you left off, organized, activated, and ready to go.
The best shortcuts are not flashy. They just disappear into your workflow. jlon
turned a multi-step start into a single, predictable command. That is all I needed.