Migrating From Bash to Zsh With Oh My Zsh Without Breaking Your Scripts (September 2026)

Migrating from bash to zsh with Oh My Zsh is one of the highest-leverage upgrades you can make to your terminal workflow. I switched recently and never looked back, but I hit the same wall everyone hits: scripts I had running for years suddenly acted weird. This guide shows you the full migration path without breaking your scripts.

You’ll learn what zsh is, how it differs from bash, exactly how to install it, how to install Oh My Zsh, and the specific fixes that keep your existing bash scripts working. By the end, you’ll have a polished shell with the safety net of a tested script environment.

Why Switch From Bash to Zsh?

People switch to zsh because it fixes the everyday annoyances of bash without forcing you to relearn shell fundamentals. The completion engine is smarter, globbing is more powerful, and the prompt can be themed far beyond bash’s PS1 tricks.

Apple made zsh the default shell on macOS Catalina because bash 3.2 was stuck on an old GPL license and zsh offered active development. Most modern Linux distributions ship zsh in their package repositories, so installation is one command away.

  • Better tab completion that suggests flags, file paths, and git branches out of the box.

  • Recursive globbing with patterns like **/*.log that work without enabling globstar.

  • Spell correction when you mistype a command name or directory.

  • Plugin ecosystem through Oh My Zsh that adds autosuggestions, syntax highlighting, and hundreds of utilities.

  • Shared history across all running shells, plus smarter history search.

For me, the killer feature was shared history. I can open ten terminal tabs and they all see the same command history. Bash can’t do that without tweaks.

Bash vs Zsh: Key Differences You Need to Know

The most important difference between bash and zsh that affects your scripts is array indexing. Bash arrays start at index 0. Zsh arrays start at index 1 by default. This is the number one cause of “it worked yesterday” bugs after migration.

Here’s the side-by-side behavior:

FeatureBashZsh
Array indexingStarts at 0Starts at 1 (by default)
Recursive globbingRequires shopt -s globstarWorks with ** out of the box
Shared historyPer-session by defaultShared across all sessions
Word splittingDefault for unquoted variablesDisabled by default (safer)
Shebang compatibility#!/bin/bash#!/bin/zsh (but bash shebang still runs bash)

Word splitting is the other trap. In bash, an unquoted variable like $files splits on whitespace automatically. Zsh keeps it as a single string, which is actually safer but breaks scripts that relied on the old behavior.

Prerequisites Before You Migrate

Before touching your shell, do three things. First, back up your existing bash configuration files. Second, list every script in your PATH that uses bash-specific syntax. Third, confirm you have admin access on your machine.

Backup these files from your home directory:

  • ~/.bashrc

  • ~/.bash_profile

  • ~/.bash_aliases

  • ~/.profile

Run ls ~/.bash* first to see what you actually have. Some users only have ~/.bashrc, others have all four.

Step 1: Check Your Current Shell

Open your terminal and run this command to see your current default shell:

echo $SHELL

If it returns /bin/bash, you’re on bash. If it returns /bin/zsh, you already have zsh set as default. On macOS Catalina and later, the default is already zsh even if your interactive shell looks like bash.

Step 2: Install Zsh on Your System

Installation is one command per platform. Run the one that matches your operating system.

On Ubuntu or Debian:

sudo apt install zsh -y

On Fedora:

sudo dnf install zsh -y

On Arch Linux or Manjaro:

sudo pacman -S zsh

On macOS, zsh is already installed at /bin/zsh. Confirm with zsh --version which should return 5.9 or newer on recent macOS.

Verify the installation:

zsh --version

Step 3: Set Zsh as Your Default Shell

The chsh command changes your login shell. Run it with the path to zsh and your username.

chsh -s $(which zsh)

You’ll be prompted for your password. After the command completes, log out and log back in for the change to take effect. New terminal windows will now open directly into zsh.

To verify it worked, open a fresh terminal and run echo $SHELL again. It should now return /bin/zsh or whatever path your system uses.

Step 4: Install Oh My Zsh

Oh My Zsh is a community-maintained configuration framework that bundles 300+ plugins, 150+ themes, and a few sane defaults. It’s the easiest on-ramp for new zsh users.

Run the official installer via curl:

sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

If you don’t have curl, use wget:

sh -c "$(wget -O- https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

The installer will back up your existing ~/.zshrc file (if one exists) and create a fresh one. It will also switch your shell to zsh if you haven’t already done that.

Tip: I always open ~/.zshrc in my editor right after install to customize the prompt, plugins, and aliases before I do anything else.

Step 5: Configure Your .zshrc File

Now move your custom bash settings into zsh. The big difference is the config file: bash uses ~/.bashrc, zsh uses ~/.zshrc.

Open ~/.zshrc and you’ll see a section at the top labeled “User configuration”. Below that, add your custom aliases, environment variables, and PATH modifications.

Common things to migrate:

  • Aliases like alias ll='ls -la' work the same way in zsh.

  • Environment variables like export EDITOR='vim' work identically.

  • PATH modifications like export PATH="$HOME/bin:$PATH" work the same.

  • Functions work but syntax for advanced features may differ.

If you had a long ~/.bashrc, you can source it from ~/.zshrc temporarily while you migrate piece by piece. Add this near the bottom of ~/.zshrc:

source ~/.bashrc

Then move individual lines into ~/.zshrc one at a time, sourcing it with source ~/.zshrc after each change.

Step 6: Choose Plugins and Themes

Oh My Zsh ships with plugins you enable by listing them in your ~/.zshrc. Find the line plugins=(git) and add plugins inside the parentheses.

Plugins I install on every new machine:

  • git for the dozens of git aliases like gst for git status.

  • zsh-autosuggestions for fish-style suggestion history. Install separately from github.com/zsh-users/zsh-autosuggestions.

  • zsh-syntax-highlighting for command syntax colors. Install from github.com/zsh-users/zsh-syntax-highlighting.

  • docker for the docker and docker-compose completions.

  • kubectl if you work with Kubernetes clusters.

For themes, ZSH_THEME="robbyrussell" is the default and looks fine. If you want more flair, install Powerlevel10k which gives you a guided configuration wizard on first run. It runs a few prompts asking about your icon preferences, then writes a fast-loading prompt config.

After editing ~/.zshrc, apply changes with source ~/.zshrc or open a new terminal.

How to Keep Your Bash Scripts Working?

This is the section most guides skip. Your existing scripts don’t automatically break when you switch your interactive shell to zsh, but they can break when you run them through zsh instead of bash. Here’s how to stay safe.

Always keep a shebang line at the top of every script. If your script starts with #!/bin/bash, the system uses bash to run it regardless of which shell you’re currently in. This is the single most important rule for cross-shell compatibility.

If you want your script to work the same way whether it’s invoked through bash or zsh, write it in POSIX sh. POSIX sh is the lowest common denominator that both bash and zsh understand. Common adjustments:

  • Use [ ] instead of bash arrays if you need cross-shell compatibility.

  • Avoid [[ ]] test syntax if the script must run on dash, ash, or other minimal shells.

  • Use $(command) instead of backticks `command` for command substitution.

  • Quote all your variables to avoid word splitting surprises.

For the array indexing problem specifically, set this option in your ~/.zshrc to make zsh behave like bash:

setopt KSH_ARRAYS

Or use the more targeted option:

setopt BASH_REMATCH

Test your scripts before going all-in. Run zsh -n script.sh to syntax-check without executing. Run zsh script.sh to actually execute it through zsh. Compare the output to running it through bash. If both produce identical output, your script is portable.

I keep a ~/scripts-test/ directory with copies of my important scripts so I can validate them in isolation without breaking production.

Common Migration Problems and Fixes

Here are the issues that show up in forums after migration, with fixes.

Locale UTF-8 errors: If you see warning: locale not set or strange characters in your prompt, fix it with export LANG=en_US.UTF-8 in your ~/.zshrc. Pick the locale that matches your system with locale -a.

Patched fonts not rendering: Powerline and Powerlevel10k need a patched font. If you see boxes instead of icons, set your terminal’s font to one like MesloLGS NF or Source Code Pro for Powerline.

PATH differences between interactive and scripts: Zsh uses different PATH handling for scripts versus interactive sessions. Always set your PATH in ~/.zshenv if you want it to apply everywhere, or in ~/.zshrc for interactive only.

Slow startup: Oh My Zsh can add 200-400ms to shell startup if you enable too many plugins. Use time zsh -i -c exit to measure. Drop any plugin you don’t actively use. Powerlevel10k is the fastest popular theme.

Plugin conflicts: If your shell misbehaves after enabling a plugin, comment it out one at a time until the problem goes away. The most common culprits are autosuggestions and syntax-highlighting when used with custom prompts.

How to Roll Back to Bash If Things Go Wrong

If your new shell environment becomes unusable, switching back is one command.

chsh -s /bin/bash

Log out and back in. Your original ~/.bashrc is still there, untouched by the zsh installation. You can also delete ~/.zshrc and ~/.oh-my-zsh to fully remove Oh My Zsh. No system files are touched during zsh installation, so rollback is always safe.

FAQ

Why do people prefer zsh over bash?

People prefer zsh over bash because it offers smarter tab completion, recursive globbing, shared history across sessions, better spell correction, and a large plugin ecosystem through frameworks like Oh My Zsh. These features reduce friction during everyday terminal work without changing how basic commands behave.

Are bash scripts compatible with zsh?

Most bash scripts work in zsh without changes, especially those that start with a #!/bin/bash shebang. The two main breaking differences are array indexing (zsh defaults to 1-indexed, bash is 0-indexed) and word splitting on unquoted variables. Add setopt KSH_ARRAYS to your .zshrc to make array behavior match bash.

How do I set zsh as my default shell?

Run chsh -s $(which zsh) and enter your password. Log out of your session and log back in for the change to take effect. Verify with echo $SHELL which should return the path to zsh.

Is zsh slower than bash?

Zsh itself is slightly faster than bash for most operations. However, frameworks like Oh My Zsh can add 200-400ms of startup time when many plugins are enabled. Disable unused plugins and consider lighter alternatives like Prezto or a hand-rolled config if startup speed matters.

Can I use Oh My Zsh without zsh?

No. Oh My Zsh is built specifically for zsh and requires zsh as your shell. If you want a similar configuration framework for bash, look at oh-my-bash or bash-it as alternatives.

What is the main difference between bash and zsh arrays?

Bash arrays start at index 0, while zsh arrays start at index 1 by default. This means arr[0] returns the first element in bash but nothing (or an error) in zsh. You can force bash-like behavior in zsh with setopt KSH_ARRAYS, or change your scripts to use arr[1] for the first element.

Conclusion

Migrating from bash to zsh with Oh My Zsh is a high-value, low-risk change when you go in stages. Install zsh first, keep bash as your default while you test, then switch over once your scripts pass zsh -n syntax checks. Add Oh My Zsh only after the bare zsh setup feels comfortable.

Your next step is to run zsh --version right now, check your existing ~/.bashrc, and start moving settings into a fresh ~/.zshrc. Within an hour you’ll have a better terminal without losing any of the scripts that keep your work running.

Leave a Comment