Short answer: the main types of shell on Linux are sh (the POSIX standard), bash (the default login shell on most distributions), dash (/bin/sh on Debian and Ubuntu), zsh (default on macOS and Kali), fish (friendly, but not POSIX), ksh and tcsh. Check yours with ps -p $$, list installed ones in /etc/shells, switch with chsh -s.
ps -p $$ -o comm= # the shell you are typing in right now
echo "$0" # usually the same; "-bash" means a login shell
echo "$SHELL" # your LOGIN shell from /etc/passwd, not the current one
cat /etc/shells # shells allowed as a login shell on this system
chsh -s /usr/bin/zsh # change your login shell (next login)The commands and outputs on this page ran on Debian 12 with bash 5.2.15 and dash 0.5.12 on 21 October 2026. zsh, fish, ksh and tcsh were not installed on that host, so what this page says about them follows their own documentation.
Types of shell compared
| Shell | Scripts | Interactive use | Default where |
|---|---|---|---|
| sh (POSIX) | The portable baseline: a standard, not one program | Minimal | /bin/sh everywhere; it points to dash, bash or BusyBox ash |
| bash | POSIX plus arrays, [[ ]], {1..9}, process substitution | History, completion, Ctrl+R search | Login shell on Debian, Ubuntu, Fedora, Rocky Linux, AlmaLinux |
| dash | POSIX only, small and fast to start | Basic: no history or tab completion on Debian | /bin/sh on Debian and Ubuntu |
| zsh | Mostly sh-compatible, with its own differences | Strong completion, globbing, themes and plugins | macOS since 10.15, Kali Linux since 2020.4 |
| fish | Own syntax, not POSIX: sh scripts do not run in it | Autosuggestions and highlighting out of the box | Nowhere by default |
| ksh | POSIX-compatible; many bash features started here | Good line editing | OpenBSD (a ksh variant), AIX |
| tcsh | C shell syntax, not Bourne-compatible | Completion, history | Long the root shell on FreeBSD |
Two families matter. The Bourne family (sh, bash, dash, ksh, zsh) shares the syntax that almost every shell script and tutorial uses. The C shell family (csh, tcsh) and fish use their own syntax, so they are fine to type in but a poor fit for scripts other people run. POSIX, the standard behind sh, is explained in Unix vs Linux.
sh, bash and dash
/bin/sh is a path that every Unix-like system has, but which program sits behind it differs. On the Debian 12 test host it is a link to dash:
$ readlink -f /bin/sh
/usr/bin/dash
$ getent passwd root | cut -d: -f7
/bin/bashSo you log in to bash, but anything that runs sh, including cron and many installer scripts, runs dash. On Fedora, Rocky Linux and AlmaLinux /bin/sh is bash itself, which then runs in POSIX mode; on Alpine it is BusyBox. A script that happens to work under one /bin/sh is not guaranteed to work under another.
zsh and fish
zsh looks almost like bash at the prompt and adds much better completion and globbing, which is why many people switch their own account to it. Scripts written for bash usually run, but not always: per the zsh documentation, unquoted variables are not split into words and arrays count from 1. fish goes further and changes the syntax (set name value instead of name=value), so keep bash or sh for scripts even if you type in fish.
Which shell am I using? $SHELL vs $0 vs ps -p $$
Three common answers, and only two of them tell you about the shell you are in:
$ dash -c 'echo "SHELL=$SHELL 0=$0"; ps -p $$ -o pid=,comm='
SHELL=/bin/bash 0=dash
887509 dash$SHELLis set at login from the last field of/etc/passwd. Starting another shell does not change it, so it still said/bin/bashinside dash.$0is the name the current shell was started with. A login shell gets a leading dash, so an SSH session shows-bash. Started withexec -a -bash bash, bash printed-bashandshopt login_shellsaidon.ps -p $$asks the kernel what process$$, the current shell’s PID, really is. This is the reliable one.
/etc/shells: which shells are installed and allowed
# /etc/shells: valid login shells
/bin/sh
/usr/bin/sh
/bin/bash
/usr/bin/bash
/bin/rbash
/usr/bin/rbash
/bin/dash
/usr/bin/dash
/usr/bin/screenPer shells(5), the file lists the full paths of valid login shells; packages add themselves when installed (here screen did), and the base shells appear twice because /bin is a link to /usr/bin on current Debian. chsh lets a normal user pick only a shell from this list, and some services, FTP daemons traditionally, refuse logins for accounts whose shell is not in it. rbash is restricted bash, a link to bash that blocks cd, changing PATH and similar.
Change your login shell with chsh
sudo apt install zsh # install it first; it adds itself to /etc/shells
chsh -s /usr/bin/zsh # your own account, asks for your password
sudo chsh -s /bin/bash deploy # another account, as root
getent passwd "$USER" | cut -d: -f7 # check what is stored nowA common reason to need it on Debian: adduser gives new accounts /bin/bash, but useradd without -s uses SHELL=/bin/sh from /etc/default/useradd, so the user logs in to dash, without history or tab completion. The change applies at the next login, not in the open session. Before you log out, open a second SSH session and check that the new shell actually starts: a typo in the path or a shell whose startup file crashes can leave you unable to log in. Service accounts that should never get a shell use /usr/sbin/nologin; the list of users that can log in shows how to spot them.
Login, interactive and non-interactive shells
bash decides which startup files to read by two questions. Per the bash manual, a login shell is one whose name starts with a dash or that was started with --login; an interactive shell reads commands from a terminal (it has i in $-). In the test, bash -c 'echo $-' printed hBc, while bash -ic printed hiBHc.
| Kind of shell | Typical case | Files bash reads |
|---|---|---|
| Interactive login | SSH login, console login | /etc/profile, then the first of ~/.bash_profile, ~/.bash_login, ~/.profile |
| Interactive, not login | New terminal tab, typing bash | /etc/bash.bashrc (Debian, Ubuntu), ~/.bashrc |
| Non-interactive | A bash script | None; only the file named in $BASH_ENV |
| Remote command | ssh host 'command' | /etc/bash.bashrc and ~/.bashrc, a special case for sshd |
| Started as sh | #!/bin/sh where sh is bash | /etc/profile and ~/.profile as login, $ENV if interactive |
That is why an alias or a PATH change that works in your terminal is missing in a script or a cron job. Debian’s default ~/.profile loads ~/.bashrc for bash, so one file covers both interactive cases. Where to put variables so that each kind of shell sees them is covered in setting environment variables on Linux.
The shebang line decides, unless you name the shell
The first line of a script, #!/bin/bash or #!/bin/sh, tells the kernel which interpreter to start when you run ./script.sh. Naming a shell on the command line overrides it. The same file under three ways of calling it:
$ cat deploy.sh
#!/bin/bash
arr=(web1 web2)
echo "second: ${arr[1]}"
$ ./deploy.sh
second: web2
$ sh deploy.sh
deploy.sh: 2: Syntax error: "(" unexpected
$ bash deploy.sh
second: web2#!/usr/bin/env bashfinds bash through PATH, useful where it is not in/bin(the BSDs, a newer bash in/usr/local/bin).#!/bin/bashis fine on any Linux server../script.shneeds the execute bit, or it fails with bash: Permission denied.- A shebang saved with Windows line endings asks for an interpreter called
bash\r, which gives bad interpreter: No such file or directory.
Why scripts should target bash or POSIX sh explicitly
A script marked #!/bin/sh but written with bash features works on a system where sh is bash and breaks on Debian and Ubuntu. Worse, it does not always stop. Under dash:
| bash feature | Under dash | POSIX replacement |
|---|---|---|
arr=(a b) | Syntax error, script stops (exit 2) | set -- a b, then $1 and $2 |
[[ "$x" == a* ]] | [[: not found, script carries on | case $x in a*) ...;; esac |
echo {1..3} | Prints {1..3} literally | seq 1 3 |
source file | source: not found | . ./file |
echo -e | Prints the -e | printf |
$RANDOM | Empty | shuf -i 0-99 -n 1 |
Pick one and say so in the first line: #!/bin/bash if you use any of the left column, #!/bin/sh only if the script passes under dash. Running it with bash --posix is not a test: in that mode bash still accepted the array. Run it with dash script.sh, or let ShellCheck (apt install shellcheck) read the shebang and flag what does not belong. The echo differences in detail are in the echo guide.
dash exists as /bin/sh for speed: in a quick loop on the test host, 200 starts of dash -c : took 0.06 seconds against 0.12 seconds for bash. That adds up for system scripts that start a shell thousands of times; for your own deploy script, bash is the more comfortable choice.
Which shell should I use?
- For typing, on a server: bash. It is already there, it is what root gets, and every guide you will follow assumes it.
- For typing, on your own machine or your own account: zsh or fish if you want better completion and suggestions. Try one first by typing
zshorfishin your current session;exitbrings you back, and nothing changes until you runchsh. - For scripts: bash with
#!/bin/bashfor anything you write for your own servers; POSIX sh with#!/bin/shonly for scripts that must run on systems you do not control, such as Alpine containers, and then test them under dash.
Shells on a server
- cron runs sh. Every crontab line goes through
/bin/sh, which is dash on Debian and Ubuntu, unless the crontab setsSHELL=/bin/bash. The crontab guide lists what else differs. - systemd runs no shell.
ExecStart=starts the program directly, so pipes,&&and redirections need an explicit/bin/bash -c '...'. - Keep root on bash. Change the shell of your own sudo user if you like zsh or fish, but most documentation and answers online assume bash, and root is the account you need to work when everything else is broken.
A Linux VPS installed from a Debian or Ubuntu template follows those distributions’ defaults, the same pair as on the test host here: bash for logins and dash as /bin/sh. Rocky Linux and CentOS Stream use bash for both. zsh and fish are one package install away. For the commands you will type in whichever shell you pick, see the Linux commands cheat sheet.
Frequently asked
What are the main types of shell in Linux?
sh (the POSIX standard), bash (the usual login shell), dash (/bin/sh on Debian and Ubuntu), zsh (default on macOS and Kali Linux), fish (friendly, with its own syntax), ksh (the KornShell) and tcsh (C shell family). sh, bash, dash, ksh and zsh share the Bourne syntax that most scripts use.
How do I check which shell I am using?
Run ps -p $$ -o comm=, which shows the shell process you are typing in. echo $0 usually shows the same name, with a leading dash for a login shell. echo $SHELL shows your login shell from /etc/passwd, which does not change when you start another shell.
What is the difference between bash and sh?
sh is the POSIX shell standard and the path /bin/sh, which is dash on Debian and Ubuntu and bash in POSIX mode on Fedora, Rocky Linux and AlmaLinux. bash adds arrays, [[ ]], brace expansion and more, so a script that uses them needs #!/bin/bash, not #!/bin/sh.
How do I change my default shell in Linux?
Install the shell, check that its path is listed in /etc/shells, then run chsh -s /usr/bin/zsh (or the path of the shell you want). It takes effect at your next login. As root, sudo chsh -s /bin/bash username changes another account.