What are the practical differences between Bash and Zsh?Bash or Zsh, what's the difference, why use one or the other?Zsh vs Bash shell in TerminalUpdate bash to version 4.0 on OSXDoes Mac OS X's bash read scripts for all users (tab-completion scripts) from /opt/local/etc/bash_completion.d path?Why so many shell in /binWhat are the differences between /usr/bin/login and /usr/bin/bash?How to use zsh and bash on iTerm2 at the same time?What are the differences between terminal types supported by iTerm2?How do I configure my Terminal app with bash and iTerm2 with zshell and oh my zsh?Suspended (tty output) when launching editors like vim, vi, emacs, or nanoWhat am I doing that causes zsh to silently change to vi mode?Problem with snippets in Zsh
Write a bot to play Grid Game
Help resolve territory acquisition design difference of opinion in MMO RTS
What would be the best propulsion system for this aircraft carrier?
Finding big cacti between Phoenix, Las Vegas, and Los Angeles
18-month-old kicked out of church nursery
Which data is "more normal"?
indent and noindent: details from Knuth's The TeXbook
How can I find the weakness of our "Quality Assurance Process"?
Translation of 見れば
Why is the Falcon Heavy center core recovery done at sea?
Why does Wonder Woman say "goodbye brother" to Ares?
How does an aircraft descend without its nose pointing down?
How to call hook_theme_suggestions_HOOK_alter twice, if node is shown twice?
Features of a Coda section
Uniqueness of bra-ket correspondence
Where do overtones in a 555 generated square wave come from?
Non-differentiable Lipschitz functions
Left a meeting without reason, what to do?
Why combine commands on a single line in a Bash script?
Building a phone charger 500 years ago
Chromatic aberration in lens
Joining matrices together
How would a young girl/boy (about 14) who never gets old survive in the 16th century?
Who originated the dangerous avocado-pitting technique?
What are the practical differences between Bash and Zsh?
Bash or Zsh, what's the difference, why use one or the other?Zsh vs Bash shell in TerminalUpdate bash to version 4.0 on OSXDoes Mac OS X's bash read scripts for all users (tab-completion scripts) from /opt/local/etc/bash_completion.d path?Why so many shell in /binWhat are the differences between /usr/bin/login and /usr/bin/bash?How to use zsh and bash on iTerm2 at the same time?What are the differences between terminal types supported by iTerm2?How do I configure my Terminal app with bash and iTerm2 with zshell and oh my zsh?Suspended (tty output) when launching editors like vim, vi, emacs, or nanoWhat am I doing that causes zsh to silently change to vi mode?Problem with snippets in Zsh
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty
margin-bottom:0;
With the news that Catalina will default to Zsh instead of Bash, I'm finding lots of results telling me about the switch, and that it may cause problems with shell scripts, but I'm not familiar enough with Zsh to know what those problems might be.
My shell scripts are really not that complicated, but I've only ever used Bash on macOS and Linux - zero experience with Zsh. Can anyone provide a simple practical comparison, or specific stumbling blocks I will need to know, so that I can start working towards being ready for the new shell when Catalina is released?
terminal bash zsh catalina
add a comment
|
With the news that Catalina will default to Zsh instead of Bash, I'm finding lots of results telling me about the switch, and that it may cause problems with shell scripts, but I'm not familiar enough with Zsh to know what those problems might be.
My shell scripts are really not that complicated, but I've only ever used Bash on macOS and Linux - zero experience with Zsh. Can anyone provide a simple practical comparison, or specific stumbling blocks I will need to know, so that I can start working towards being ready for the new shell when Catalina is released?
terminal bash zsh catalina
16
All this hubbub that you have been reading is much to do about nothing. The OS assigns a "default" shell when creating new users, no reason more . Bash isn't going away, and you can use that as your shell or any of the other shells currently offered.
– fd0
Jun 8 at 12:27
1
Speaking as someone who's used both and landed on bash -- the only thing that made me really, deeply unhappy with zsh was its decision to break POSIX compliance when the standard canonicalizes admittedly-bad design decisions in ways that made it easy to get sloppy about correctness when trying to write scripts that needed to be compatible with other, strictly-POSIX-superset shells. Unfortunately, that "only thing" can be a pretty big one. Still, ksh93 isn't going away, and anyone serious about bash wouldn't use the ancient 3.x release Apple ships anyhow.
– Charles Duffy
Jun 10 at 16:29
As a follow-up - installed Catalina yesterday, switched to zsh, imported bash_history and copied over some of my preferred aliases from bash_profile, nothing seems to have broken. Appreciate all of the information provided by everyone here and hopefully it helps others out as well.
– dr.nixon
Oct 9 at 17:07
add a comment
|
With the news that Catalina will default to Zsh instead of Bash, I'm finding lots of results telling me about the switch, and that it may cause problems with shell scripts, but I'm not familiar enough with Zsh to know what those problems might be.
My shell scripts are really not that complicated, but I've only ever used Bash on macOS and Linux - zero experience with Zsh. Can anyone provide a simple practical comparison, or specific stumbling blocks I will need to know, so that I can start working towards being ready for the new shell when Catalina is released?
terminal bash zsh catalina
With the news that Catalina will default to Zsh instead of Bash, I'm finding lots of results telling me about the switch, and that it may cause problems with shell scripts, but I'm not familiar enough with Zsh to know what those problems might be.
My shell scripts are really not that complicated, but I've only ever used Bash on macOS and Linux - zero experience with Zsh. Can anyone provide a simple practical comparison, or specific stumbling blocks I will need to know, so that I can start working towards being ready for the new shell when Catalina is released?
terminal bash zsh catalina
terminal bash zsh catalina
edited Jul 21 at 17:03
JakeGould
3,8226 gold badges22 silver badges42 bronze badges
3,8226 gold badges22 silver badges42 bronze badges
asked Jun 7 at 21:52
dr.nixondr.nixon
2,2131 gold badge13 silver badges19 bronze badges
2,2131 gold badge13 silver badges19 bronze badges
16
All this hubbub that you have been reading is much to do about nothing. The OS assigns a "default" shell when creating new users, no reason more . Bash isn't going away, and you can use that as your shell or any of the other shells currently offered.
– fd0
Jun 8 at 12:27
1
Speaking as someone who's used both and landed on bash -- the only thing that made me really, deeply unhappy with zsh was its decision to break POSIX compliance when the standard canonicalizes admittedly-bad design decisions in ways that made it easy to get sloppy about correctness when trying to write scripts that needed to be compatible with other, strictly-POSIX-superset shells. Unfortunately, that "only thing" can be a pretty big one. Still, ksh93 isn't going away, and anyone serious about bash wouldn't use the ancient 3.x release Apple ships anyhow.
– Charles Duffy
Jun 10 at 16:29
As a follow-up - installed Catalina yesterday, switched to zsh, imported bash_history and copied over some of my preferred aliases from bash_profile, nothing seems to have broken. Appreciate all of the information provided by everyone here and hopefully it helps others out as well.
– dr.nixon
Oct 9 at 17:07
add a comment
|
16
All this hubbub that you have been reading is much to do about nothing. The OS assigns a "default" shell when creating new users, no reason more . Bash isn't going away, and you can use that as your shell or any of the other shells currently offered.
– fd0
Jun 8 at 12:27
1
Speaking as someone who's used both and landed on bash -- the only thing that made me really, deeply unhappy with zsh was its decision to break POSIX compliance when the standard canonicalizes admittedly-bad design decisions in ways that made it easy to get sloppy about correctness when trying to write scripts that needed to be compatible with other, strictly-POSIX-superset shells. Unfortunately, that "only thing" can be a pretty big one. Still, ksh93 isn't going away, and anyone serious about bash wouldn't use the ancient 3.x release Apple ships anyhow.
– Charles Duffy
Jun 10 at 16:29
As a follow-up - installed Catalina yesterday, switched to zsh, imported bash_history and copied over some of my preferred aliases from bash_profile, nothing seems to have broken. Appreciate all of the information provided by everyone here and hopefully it helps others out as well.
– dr.nixon
Oct 9 at 17:07
16
16
All this hubbub that you have been reading is much to do about nothing. The OS assigns a "default" shell when creating new users, no reason more . Bash isn't going away, and you can use that as your shell or any of the other shells currently offered.
– fd0
Jun 8 at 12:27
All this hubbub that you have been reading is much to do about nothing. The OS assigns a "default" shell when creating new users, no reason more . Bash isn't going away, and you can use that as your shell or any of the other shells currently offered.
– fd0
Jun 8 at 12:27
1
1
Speaking as someone who's used both and landed on bash -- the only thing that made me really, deeply unhappy with zsh was its decision to break POSIX compliance when the standard canonicalizes admittedly-bad design decisions in ways that made it easy to get sloppy about correctness when trying to write scripts that needed to be compatible with other, strictly-POSIX-superset shells. Unfortunately, that "only thing" can be a pretty big one. Still, ksh93 isn't going away, and anyone serious about bash wouldn't use the ancient 3.x release Apple ships anyhow.
– Charles Duffy
Jun 10 at 16:29
Speaking as someone who's used both and landed on bash -- the only thing that made me really, deeply unhappy with zsh was its decision to break POSIX compliance when the standard canonicalizes admittedly-bad design decisions in ways that made it easy to get sloppy about correctness when trying to write scripts that needed to be compatible with other, strictly-POSIX-superset shells. Unfortunately, that "only thing" can be a pretty big one. Still, ksh93 isn't going away, and anyone serious about bash wouldn't use the ancient 3.x release Apple ships anyhow.
– Charles Duffy
Jun 10 at 16:29
As a follow-up - installed Catalina yesterday, switched to zsh, imported bash_history and copied over some of my preferred aliases from bash_profile, nothing seems to have broken. Appreciate all of the information provided by everyone here and hopefully it helps others out as well.
– dr.nixon
Oct 9 at 17:07
As a follow-up - installed Catalina yesterday, switched to zsh, imported bash_history and copied over some of my preferred aliases from bash_profile, nothing seems to have broken. Appreciate all of the information provided by everyone here and hopefully it helps others out as well.
– dr.nixon
Oct 9 at 17:07
add a comment
|
4 Answers
4
active
oldest
votes
First, some important things:
Bash isn't going away. If you're already using bash, nothing will change for you. All that changes is that zsh will be the default login shell for new accounts, and even then, you can select bash instead.
Scripts are not affected. What changes is the shell for interactive use, i.e. the shell in terminals (and also a few other things that use the login shell, such as crontabs). If you have a script in a file with execution permissions, starting with a shebang line such as#!/bin/bashor#!/bin/shor#!/usr/bin/env bash, it'll keep working exactly as before.- Zsh's syntax is not completely compatible with bash, but it's close. A lot of code will keep working, for example typical aliases and functions. The main differences are in interactive configuration features.
Now, assuming you're considering switching to zsh, which has been a possibility for years, here are the main differences you'll encounter. This is not an exhaustive list!
Main differences for interactive use
Configuration files: bash reads (mainly) .bashrc in non-login interactive shells (but macOS starts a login shell in terminals by default), .profile or .bash_profile in login shells, and .inputrc. Zsh reads (mainly) .zshrc (in all interactive shells) and .zprofile (in login shells). This means that none of your bash customizations will apply: you'll need to port them over. You can't just copy the files because many things will need tweaking.
Key bindings use completely different syntax. Bash uses .inputrc and the bind builtin to bind keys to readline commands. Zsh uses the bindkey builtin to bind keys to zle widgets. Most readline commands have a zsh equivalent, but it isn't always a perfect equivalence.
Speaking of key bindings, if you use Vi(m) as your in-terminal editor but not as your command line mode in the shell, you'll notice zsh defaults to vi editing mode if EDITOR or VISUAL is set to vi or vim. bindkey -e switches to emacs mode.
Prompt: bash sets the prompt (mainly) from PS1 which contains backslash escapes. Zsh sets the prompt mainly from PS1 which contains percent escapes. The functionality of bash's PROMPT_COMMAND is available in zsh via the precmd and preexec hook functions. Zsh has more convenience mechanisms to build fancy prompts including a prompt theme mechanism.
The basic command line history mechanisms (navigation with Up/Down, search with Ctrl+R, history expansion with !! and friends, last argument recall with Alt+. or $_) work in the same way, but there are a lot of differences in the details, too many to list here. You can copy your .bash_history to .zsh_history if you haven't changed a shell option that changes the file format.
Completion: both shells default to a basic completion mode that mostly completes command and file names, and switch to a fancy mode by including bash_completion on bash or by running compinit in zsh. You'll find some commands that bash handles better and some that zsh handles better. Zsh is usually more precise, but sometimes gives up where bash does something that isn't correct but is sensible. To specify possible completions for a command, zsh has three mechanisms:
- The “old” completion mechanism with
compctlwhich you can forget about. - The “new” completion mechanism with
compaddand lots of functions that begin with underscore and a powerful but complex user configuration mechanism. - An emulation to support bash completion functions which you can enable by running
bashcompinit. The emulation isn't 100% perfect but it usually works.
Many of bash's shopt settings have a corresponding setopt in zsh.
Zsh doesn't treat # as a comment start on the command line by default, only in scripts (including .zshrc and such). To enable interactive comments, run setopt interactive_comments.
Main differences for scripting
(and for power users on the command line of course)
In bash, $foo takes the value of foo, splits it at whitespace characters, and for each whitespace-separated part, if it contains wildcard characters and matches an existing file, replaces the pattern by the list of matches. To just get the value of foo, you need "$foo". The same applies to command substitution $(foo). In zsh, $foo is the value of foo and $(foo) is the output of foo minus its final newlines, with two exceptions. If a word becomes empty due to expanding empty unquoted variables, it's removed (e.g. a=; b=; printf "%sn" one "$a$b" three $a$b five prints one, an empty line, three, five). The result of an unquoted command substitution is split at whitespace but the pieces don't undergo wildcard matching.
Bash arrays are indexed from 0 to (length-1). Zsh arrays are indexed from 1 to length. With a=(one two three), in bash, $a[1] is two, but in zsh, it's one. In bash, if you just reference an array variable without braces, you get the first element, e.g. $a is one and $a[1] is one[1]. In zsh, $a expands to the list of non-empty elements, and $a[1] expands to the first element. Similarly, in bash, the length of an array is $#a; this also works in zsh but you can write it more simply as $#a. You can make 0-indexing the default with setopt ksh_arrays; this also turns on the requirement to use braces to refer to an array element.
Bash has extra wildcard patterns such as @(foo|bar) to match foo or bar, which are only enabled with shopt -s extglob. In zsh, you can enable these patterns with setopt ksh_glob, but there's also a simpler-to-type native syntax such as (foo|bar), some of which requires setopt extended_glob (do put that in your .zshrc, and it's on by default in completion functions). **/ for recursive directory traversal is always enabled in zsh.
In bash, by default, if a wildcard pattern doesn't match any file, it's left unchanged. In zsh, by default, you'll get an error, which is usually the safest setting. If you want to pass a wildcard parameter to a command, use quotes. You can switch to the bash behavior with setopt no_nomatch. You can make non-matching wildcard patterns expand to an empty list instead with setopt null_glob.
In bash, the right-hand side of a pipeline runs in a subshell. In zsh, it runs in the parent shell, so you can write things like somecommand | read output.
Some nice zsh features
Here are a few nice zsh features that bash doesn't have (at least not without some serious elbow grease). Once again, this is just a selection of the ones I consider the most useful.
Glob qualifiers allow matching files based on metadata such as their time stamp, their size, etc. They also allow tweaking the output. The syntax is rather cryptic, but it's extremely convenient. Here are a few examples:
foo*(.): only regular files matchingfoo*and symbolic links to regular files, not directories and other special files.foo*(*.): only executable regular files matchingfoo*.foo*(-.): only regular files matchingfoo*, not symbolic links and other special files.foo*(-@): only dangling symbolic links matchingfoo*.foo*(om): the files matchingfoo*, sorted by last modification date, most recent first. Note that if you pass this tols, it'll do its own sorting. This is especially useful in…foo*(om[1,10]): the 10 most recent files matchingfoo*, most recent first.foo*(Lm+1): files matchingfoo*whose size is at least 1MB.foo*(N): same asfoo*, but if this doesn't match any file, produce an empty list regardless of the setting of thenull_globoption (see above).*(D): match all files including dot files (except.and..).foo/bar/*(:t)(using a history modifier): the files infoo/bar, but with only the base name of the file. E.g. if there is afoo/bar/qux.txt, it's expanded asqux.txt.foo/bar/*(.:r): take regular files underfoo/barand remove the extension. E.g.foo/bar/qux.txtis expanded asfoo/bar/qux.foo*.odt(e''REPLY=$REPLY:r.pdf''): take the list of files matchingfoo*.odt, and replace.odtby.pdf(regardless of whether the PDF file exists).
Here are a few useful zsh-specific wildcard patterns.
foo*.txt~foobar*: all.txtfiles whose name starts withfoobut notfoobar.image<->.jpg(n): all.jpgfiles whose base name isimagefollowed by a number, e.g.image3.jpgandimage22.jpgbut notimage-backup.jpg. The glob qualifier(n)causes the files to be listed in numerical order, i.e.image9.jpgcomes beforeimage10.jpg(you can make this the default even without-nwithsetopt numeric_glob_sort).
To mass-rename files, zsh provides a very convenient tool: the zmv function. Suggested for your .zshrc:
autoload zmv
alias zcp='zmv -C' zln='zmv -L'
Example:
zmv '(*).jpeg' '$1.jpg'
zmv '(*)-backup.(*)' 'backups/$1.$2'
Bash has a few ways to apply transformations when taking the value of a variable. Zsh has some of the same and many more.
Zsh has a number of little convenient features to change directories. Turn on setopt auto_cd to change to a directory when you type its name without having to type cd (bash also has this nowadays). You can use the two-argument form to cd to change to a directory whose name is close to the current directory. For example, if you're in /some/where/foo-old/deeply/nested/inside and you want to go to /some/where/foo-new/deeply/nested/inside, just type cd old new.
To assign a value to a variable, you of course write VARIABLE=VALUE. To edit the value of a variable interactively, just run vared VARIABLE.
Final advice
Zsh comes with a configuration interface that supports a few of the most common settings, including canned recipes for things like case-insensitive completion. To (re)run this interface (the first line is not needed if you're using a configuration file that was edited by zsh-newuser-install):
autoload -U zsh-newuser-install
zsh-newuser-install
Out of the box, with no configuration file at all, many of zsh's useful features are disabled for backward compatibility with 1990's versions. zsh-newuser-install suggests some recommended features to turn on.
There are many zsh configuration frameworks on the web (many of them are on Github). They can be a convenient way to get started with some powerful features. The flip side of the coin is they often lock you in doing things the way the author does, so sometimes they'll prevent you from doing things the way you want. Use them at your own risk.
The zsh manual has a lot of information, but it's often written in a way that's terse and hard to follow, and has few examples. Don't hesitate to search for explanations and examples online: if you only use the part of zsh that's easy to understand in the manual, you'll miss out. Two good resources are the zsh-users mailing list and Unix Stack Exchange.
9
Epic answer! Thank you!
– lejonet
Jun 9 at 8:30
Now I'm wondering if the amazing ctrl-o works on zsh. Of course, it doesn't work on Mac OS on bash either, so it's not really relevant to this answer or site. I couldn't find any information on ctrl-o in zsh in a quick online search, but then again, the information on ctrl-o in bash is also universally inaccurate...
– Jasper
Jun 9 at 15:04
@Jasper I didn't know bash had this. Going by the description, C-o does the same thing in zsh with the default key bindings.
– Gilles
Jun 9 at 23:18
I was glad to see that you included the "Some nice zsh features" section, and anxiously read it to try and understand why Apple might have made the decision to switch away from the extremely-common Bash to the much less popular Zsh. I didn't find anything at all even remotely compelling there to justify the switch. It's obviously a non-exhaustive list, but were you omitting the headline features of Zsh because they're supposed to be obvious? What am I missing here?
– Cody Gray
Jun 9 at 23:58
1
@CodyGray I don't know and Apple isn't in the habit of justifying themselves. It may have had something to do with the fact that if the situation had been reversed, there wouldn't be a “nice bash features” section. Or it may be because the last non-GPLv3 bash is getting really old whereas zsh has a more liberal license.
– Gilles
Jun 10 at 0:06
|
show 3 more comments
Change your shell now and test - no need to wait.
chsh -s /bin/zsh
- All the scripts that depend on
bashsyntax will still find and call bash. - the same bash from Mojave is shipping on Catalina and migrated users keep their old shell.
Also, I would estimate 95% of macOS users don't use a command line and of those that do, another 95% won't have to change anything significant or at all. (I'd wager it's more like 10% of the 1% that know shells exist need to do anything other than port a couple lines in their .dot files)
Your prompt will change and if you changed your prompt on bash, the way to change it on zsh is no harder and no less documented than bash.
The newer shells would fail to ever get off the ground if they broke major items or caused a painful adaptation period. If you want a more fundamental change and really want a shell you need to think about and requires training and intention to adopt - try fish.
I am conflicted withfish. I use it for two years now but the incompatibility of some copy/pasted one-lines is tiring. On the other hand it is a very handy shell (the automatic suggestions without <kbd>Tab</kbd> are excellent)
– WoJ
Jun 8 at 10:08
I wanted to love fish, I still want to love fish, but I have too many shell constructs from a decade inkshto ever really leave that fold. I’ll gladly leave bash behind and fully embrace zsh now, personally.
– bmike♦
Jun 8 at 14:33
1
I am thoroughly underwhelmed with fish. It is really just yet another Unix-like shell with a little bit less cruft. (It literally says "Finally, a command line shell for the 90s" on the homepage, after all.) The only new shell that I have seen in recent years that actually did bring something new to the (mainstream) table, was PowerShell, which unfortunately was confined to Windows much too long, and is still confined to .NET. There still is not that much new in PowerShell that hasn't already been done e.g. in DCL or JCL, but it has been done in a (somewhat) tasteful way.
– Jörg W Mittag
Jun 8 at 14:45
@bmike Why not just use ksh, then? Apple ships the most current version. Myself, I punted bash long ago and see no reason to start using zsh now.
– Marc Wilson
Jun 8 at 23:46
4
This answer doesn't describe the differences between bash and zsh at all though... and I'm not quite sure why specifying that "95% of macOS users don't use command line" is relevant for a question of a user which obviously does use bash.
– Mavrik
Jun 9 at 9:05
|
show 1 more comment
My shell scripts are really not that complicated
Do your shell scripts have shebang lines (begin with #! /bin/bash or similar)? If not, you might have unintentionally been using a bash feature, where it runs scripts without a shebang using bash. Other shells, like dash or zsh, leave it up to the OS, which would usually use /bin/sh instead. /bin/sh on macOS is, and probably will remain, a copy of /bin/bash, but executing bash with the name sh causes it to have different behaviour.
The specifics are the Bash manual, 6.11 Bash POSIX mode. Some points:
- Bash ensures that the
POSIXLY_CORRECTvariable is set.
This environment variable may affect the behaviour of a number of other tools, especially if you have GNU tools installed.
- Process substitution is not available.
Process substitution is the <(...) or >(...) syntax.
- The
.andsourcebuiltins do not search the current directory for the filename argument if it is not found by searching PATH.
So if your script did . foo expecting it to source a file named foo in the current directory, that won't work. You should do . ./foo, instead.
As you can guess from the numbers, there are a lot of minor differences in behaviour of bash in POSIX mode. Best use a shebang if you mean to use bash for your scripts.
1
In checking it looks like the vast majority of the shell scripts I wrote or use either explicitly state /bin/bash in the shebang (very few of them) or state /bin/sh (nearly all of them), so that at least should not be an issue. Thanks.
– dr.nixon
Jun 10 at 21:25
add a comment
|
In the spirit of keeping things simple...
Can anyone provide a simple practical comparison, or specific stumbling blocks I will need to know, so that I can start working towards being ready for the new shell when Catalina is released?
If you're thinking using the new default shell consider:
- If you want to give Zsh a whirl and feel some of the differences without changing shell settings on your machine you might try Powerline10k in a Docker container and see if it is your cup of tea.
- If you don't need all the bells and whistles and use Bash for just basic scripts it's rather easy to set your shell as others here have explained. And if you decide you want to use features in Bash 5 it's a fairly trivial upgrade for macOS.
- If you want to improve the portability of your scripts so they're more likely to function as expected in both shells, test them for POSIX-compliance and remove any "bashisms". I've used ShellCheck for this and it works quite well for less complicated scripts.
While no particular path is clear these three approaches should give you enough confidence to make an informed decision without over engineering the problem or solution space.
add a comment
|
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
First, some important things:
Bash isn't going away. If you're already using bash, nothing will change for you. All that changes is that zsh will be the default login shell for new accounts, and even then, you can select bash instead.
Scripts are not affected. What changes is the shell for interactive use, i.e. the shell in terminals (and also a few other things that use the login shell, such as crontabs). If you have a script in a file with execution permissions, starting with a shebang line such as#!/bin/bashor#!/bin/shor#!/usr/bin/env bash, it'll keep working exactly as before.- Zsh's syntax is not completely compatible with bash, but it's close. A lot of code will keep working, for example typical aliases and functions. The main differences are in interactive configuration features.
Now, assuming you're considering switching to zsh, which has been a possibility for years, here are the main differences you'll encounter. This is not an exhaustive list!
Main differences for interactive use
Configuration files: bash reads (mainly) .bashrc in non-login interactive shells (but macOS starts a login shell in terminals by default), .profile or .bash_profile in login shells, and .inputrc. Zsh reads (mainly) .zshrc (in all interactive shells) and .zprofile (in login shells). This means that none of your bash customizations will apply: you'll need to port them over. You can't just copy the files because many things will need tweaking.
Key bindings use completely different syntax. Bash uses .inputrc and the bind builtin to bind keys to readline commands. Zsh uses the bindkey builtin to bind keys to zle widgets. Most readline commands have a zsh equivalent, but it isn't always a perfect equivalence.
Speaking of key bindings, if you use Vi(m) as your in-terminal editor but not as your command line mode in the shell, you'll notice zsh defaults to vi editing mode if EDITOR or VISUAL is set to vi or vim. bindkey -e switches to emacs mode.
Prompt: bash sets the prompt (mainly) from PS1 which contains backslash escapes. Zsh sets the prompt mainly from PS1 which contains percent escapes. The functionality of bash's PROMPT_COMMAND is available in zsh via the precmd and preexec hook functions. Zsh has more convenience mechanisms to build fancy prompts including a prompt theme mechanism.
The basic command line history mechanisms (navigation with Up/Down, search with Ctrl+R, history expansion with !! and friends, last argument recall with Alt+. or $_) work in the same way, but there are a lot of differences in the details, too many to list here. You can copy your .bash_history to .zsh_history if you haven't changed a shell option that changes the file format.
Completion: both shells default to a basic completion mode that mostly completes command and file names, and switch to a fancy mode by including bash_completion on bash or by running compinit in zsh. You'll find some commands that bash handles better and some that zsh handles better. Zsh is usually more precise, but sometimes gives up where bash does something that isn't correct but is sensible. To specify possible completions for a command, zsh has three mechanisms:
- The “old” completion mechanism with
compctlwhich you can forget about. - The “new” completion mechanism with
compaddand lots of functions that begin with underscore and a powerful but complex user configuration mechanism. - An emulation to support bash completion functions which you can enable by running
bashcompinit. The emulation isn't 100% perfect but it usually works.
Many of bash's shopt settings have a corresponding setopt in zsh.
Zsh doesn't treat # as a comment start on the command line by default, only in scripts (including .zshrc and such). To enable interactive comments, run setopt interactive_comments.
Main differences for scripting
(and for power users on the command line of course)
In bash, $foo takes the value of foo, splits it at whitespace characters, and for each whitespace-separated part, if it contains wildcard characters and matches an existing file, replaces the pattern by the list of matches. To just get the value of foo, you need "$foo". The same applies to command substitution $(foo). In zsh, $foo is the value of foo and $(foo) is the output of foo minus its final newlines, with two exceptions. If a word becomes empty due to expanding empty unquoted variables, it's removed (e.g. a=; b=; printf "%sn" one "$a$b" three $a$b five prints one, an empty line, three, five). The result of an unquoted command substitution is split at whitespace but the pieces don't undergo wildcard matching.
Bash arrays are indexed from 0 to (length-1). Zsh arrays are indexed from 1 to length. With a=(one two three), in bash, $a[1] is two, but in zsh, it's one. In bash, if you just reference an array variable without braces, you get the first element, e.g. $a is one and $a[1] is one[1]. In zsh, $a expands to the list of non-empty elements, and $a[1] expands to the first element. Similarly, in bash, the length of an array is $#a; this also works in zsh but you can write it more simply as $#a. You can make 0-indexing the default with setopt ksh_arrays; this also turns on the requirement to use braces to refer to an array element.
Bash has extra wildcard patterns such as @(foo|bar) to match foo or bar, which are only enabled with shopt -s extglob. In zsh, you can enable these patterns with setopt ksh_glob, but there's also a simpler-to-type native syntax such as (foo|bar), some of which requires setopt extended_glob (do put that in your .zshrc, and it's on by default in completion functions). **/ for recursive directory traversal is always enabled in zsh.
In bash, by default, if a wildcard pattern doesn't match any file, it's left unchanged. In zsh, by default, you'll get an error, which is usually the safest setting. If you want to pass a wildcard parameter to a command, use quotes. You can switch to the bash behavior with setopt no_nomatch. You can make non-matching wildcard patterns expand to an empty list instead with setopt null_glob.
In bash, the right-hand side of a pipeline runs in a subshell. In zsh, it runs in the parent shell, so you can write things like somecommand | read output.
Some nice zsh features
Here are a few nice zsh features that bash doesn't have (at least not without some serious elbow grease). Once again, this is just a selection of the ones I consider the most useful.
Glob qualifiers allow matching files based on metadata such as their time stamp, their size, etc. They also allow tweaking the output. The syntax is rather cryptic, but it's extremely convenient. Here are a few examples:
foo*(.): only regular files matchingfoo*and symbolic links to regular files, not directories and other special files.foo*(*.): only executable regular files matchingfoo*.foo*(-.): only regular files matchingfoo*, not symbolic links and other special files.foo*(-@): only dangling symbolic links matchingfoo*.foo*(om): the files matchingfoo*, sorted by last modification date, most recent first. Note that if you pass this tols, it'll do its own sorting. This is especially useful in…foo*(om[1,10]): the 10 most recent files matchingfoo*, most recent first.foo*(Lm+1): files matchingfoo*whose size is at least 1MB.foo*(N): same asfoo*, but if this doesn't match any file, produce an empty list regardless of the setting of thenull_globoption (see above).*(D): match all files including dot files (except.and..).foo/bar/*(:t)(using a history modifier): the files infoo/bar, but with only the base name of the file. E.g. if there is afoo/bar/qux.txt, it's expanded asqux.txt.foo/bar/*(.:r): take regular files underfoo/barand remove the extension. E.g.foo/bar/qux.txtis expanded asfoo/bar/qux.foo*.odt(e''REPLY=$REPLY:r.pdf''): take the list of files matchingfoo*.odt, and replace.odtby.pdf(regardless of whether the PDF file exists).
Here are a few useful zsh-specific wildcard patterns.
foo*.txt~foobar*: all.txtfiles whose name starts withfoobut notfoobar.image<->.jpg(n): all.jpgfiles whose base name isimagefollowed by a number, e.g.image3.jpgandimage22.jpgbut notimage-backup.jpg. The glob qualifier(n)causes the files to be listed in numerical order, i.e.image9.jpgcomes beforeimage10.jpg(you can make this the default even without-nwithsetopt numeric_glob_sort).
To mass-rename files, zsh provides a very convenient tool: the zmv function. Suggested for your .zshrc:
autoload zmv
alias zcp='zmv -C' zln='zmv -L'
Example:
zmv '(*).jpeg' '$1.jpg'
zmv '(*)-backup.(*)' 'backups/$1.$2'
Bash has a few ways to apply transformations when taking the value of a variable. Zsh has some of the same and many more.
Zsh has a number of little convenient features to change directories. Turn on setopt auto_cd to change to a directory when you type its name without having to type cd (bash also has this nowadays). You can use the two-argument form to cd to change to a directory whose name is close to the current directory. For example, if you're in /some/where/foo-old/deeply/nested/inside and you want to go to /some/where/foo-new/deeply/nested/inside, just type cd old new.
To assign a value to a variable, you of course write VARIABLE=VALUE. To edit the value of a variable interactively, just run vared VARIABLE.
Final advice
Zsh comes with a configuration interface that supports a few of the most common settings, including canned recipes for things like case-insensitive completion. To (re)run this interface (the first line is not needed if you're using a configuration file that was edited by zsh-newuser-install):
autoload -U zsh-newuser-install
zsh-newuser-install
Out of the box, with no configuration file at all, many of zsh's useful features are disabled for backward compatibility with 1990's versions. zsh-newuser-install suggests some recommended features to turn on.
There are many zsh configuration frameworks on the web (many of them are on Github). They can be a convenient way to get started with some powerful features. The flip side of the coin is they often lock you in doing things the way the author does, so sometimes they'll prevent you from doing things the way you want. Use them at your own risk.
The zsh manual has a lot of information, but it's often written in a way that's terse and hard to follow, and has few examples. Don't hesitate to search for explanations and examples online: if you only use the part of zsh that's easy to understand in the manual, you'll miss out. Two good resources are the zsh-users mailing list and Unix Stack Exchange.
9
Epic answer! Thank you!
– lejonet
Jun 9 at 8:30
Now I'm wondering if the amazing ctrl-o works on zsh. Of course, it doesn't work on Mac OS on bash either, so it's not really relevant to this answer or site. I couldn't find any information on ctrl-o in zsh in a quick online search, but then again, the information on ctrl-o in bash is also universally inaccurate...
– Jasper
Jun 9 at 15:04
@Jasper I didn't know bash had this. Going by the description, C-o does the same thing in zsh with the default key bindings.
– Gilles
Jun 9 at 23:18
I was glad to see that you included the "Some nice zsh features" section, and anxiously read it to try and understand why Apple might have made the decision to switch away from the extremely-common Bash to the much less popular Zsh. I didn't find anything at all even remotely compelling there to justify the switch. It's obviously a non-exhaustive list, but were you omitting the headline features of Zsh because they're supposed to be obvious? What am I missing here?
– Cody Gray
Jun 9 at 23:58
1
@CodyGray I don't know and Apple isn't in the habit of justifying themselves. It may have had something to do with the fact that if the situation had been reversed, there wouldn't be a “nice bash features” section. Or it may be because the last non-GPLv3 bash is getting really old whereas zsh has a more liberal license.
– Gilles
Jun 10 at 0:06
|
show 3 more comments
First, some important things:
Bash isn't going away. If you're already using bash, nothing will change for you. All that changes is that zsh will be the default login shell for new accounts, and even then, you can select bash instead.
Scripts are not affected. What changes is the shell for interactive use, i.e. the shell in terminals (and also a few other things that use the login shell, such as crontabs). If you have a script in a file with execution permissions, starting with a shebang line such as#!/bin/bashor#!/bin/shor#!/usr/bin/env bash, it'll keep working exactly as before.- Zsh's syntax is not completely compatible with bash, but it's close. A lot of code will keep working, for example typical aliases and functions. The main differences are in interactive configuration features.
Now, assuming you're considering switching to zsh, which has been a possibility for years, here are the main differences you'll encounter. This is not an exhaustive list!
Main differences for interactive use
Configuration files: bash reads (mainly) .bashrc in non-login interactive shells (but macOS starts a login shell in terminals by default), .profile or .bash_profile in login shells, and .inputrc. Zsh reads (mainly) .zshrc (in all interactive shells) and .zprofile (in login shells). This means that none of your bash customizations will apply: you'll need to port them over. You can't just copy the files because many things will need tweaking.
Key bindings use completely different syntax. Bash uses .inputrc and the bind builtin to bind keys to readline commands. Zsh uses the bindkey builtin to bind keys to zle widgets. Most readline commands have a zsh equivalent, but it isn't always a perfect equivalence.
Speaking of key bindings, if you use Vi(m) as your in-terminal editor but not as your command line mode in the shell, you'll notice zsh defaults to vi editing mode if EDITOR or VISUAL is set to vi or vim. bindkey -e switches to emacs mode.
Prompt: bash sets the prompt (mainly) from PS1 which contains backslash escapes. Zsh sets the prompt mainly from PS1 which contains percent escapes. The functionality of bash's PROMPT_COMMAND is available in zsh via the precmd and preexec hook functions. Zsh has more convenience mechanisms to build fancy prompts including a prompt theme mechanism.
The basic command line history mechanisms (navigation with Up/Down, search with Ctrl+R, history expansion with !! and friends, last argument recall with Alt+. or $_) work in the same way, but there are a lot of differences in the details, too many to list here. You can copy your .bash_history to .zsh_history if you haven't changed a shell option that changes the file format.
Completion: both shells default to a basic completion mode that mostly completes command and file names, and switch to a fancy mode by including bash_completion on bash or by running compinit in zsh. You'll find some commands that bash handles better and some that zsh handles better. Zsh is usually more precise, but sometimes gives up where bash does something that isn't correct but is sensible. To specify possible completions for a command, zsh has three mechanisms:
- The “old” completion mechanism with
compctlwhich you can forget about. - The “new” completion mechanism with
compaddand lots of functions that begin with underscore and a powerful but complex user configuration mechanism. - An emulation to support bash completion functions which you can enable by running
bashcompinit. The emulation isn't 100% perfect but it usually works.
Many of bash's shopt settings have a corresponding setopt in zsh.
Zsh doesn't treat # as a comment start on the command line by default, only in scripts (including .zshrc and such). To enable interactive comments, run setopt interactive_comments.
Main differences for scripting
(and for power users on the command line of course)
In bash, $foo takes the value of foo, splits it at whitespace characters, and for each whitespace-separated part, if it contains wildcard characters and matches an existing file, replaces the pattern by the list of matches. To just get the value of foo, you need "$foo". The same applies to command substitution $(foo). In zsh, $foo is the value of foo and $(foo) is the output of foo minus its final newlines, with two exceptions. If a word becomes empty due to expanding empty unquoted variables, it's removed (e.g. a=; b=; printf "%sn" one "$a$b" three $a$b five prints one, an empty line, three, five). The result of an unquoted command substitution is split at whitespace but the pieces don't undergo wildcard matching.
Bash arrays are indexed from 0 to (length-1). Zsh arrays are indexed from 1 to length. With a=(one two three), in bash, $a[1] is two, but in zsh, it's one. In bash, if you just reference an array variable without braces, you get the first element, e.g. $a is one and $a[1] is one[1]. In zsh, $a expands to the list of non-empty elements, and $a[1] expands to the first element. Similarly, in bash, the length of an array is $#a; this also works in zsh but you can write it more simply as $#a. You can make 0-indexing the default with setopt ksh_arrays; this also turns on the requirement to use braces to refer to an array element.
Bash has extra wildcard patterns such as @(foo|bar) to match foo or bar, which are only enabled with shopt -s extglob. In zsh, you can enable these patterns with setopt ksh_glob, but there's also a simpler-to-type native syntax such as (foo|bar), some of which requires setopt extended_glob (do put that in your .zshrc, and it's on by default in completion functions). **/ for recursive directory traversal is always enabled in zsh.
In bash, by default, if a wildcard pattern doesn't match any file, it's left unchanged. In zsh, by default, you'll get an error, which is usually the safest setting. If you want to pass a wildcard parameter to a command, use quotes. You can switch to the bash behavior with setopt no_nomatch. You can make non-matching wildcard patterns expand to an empty list instead with setopt null_glob.
In bash, the right-hand side of a pipeline runs in a subshell. In zsh, it runs in the parent shell, so you can write things like somecommand | read output.
Some nice zsh features
Here are a few nice zsh features that bash doesn't have (at least not without some serious elbow grease). Once again, this is just a selection of the ones I consider the most useful.
Glob qualifiers allow matching files based on metadata such as their time stamp, their size, etc. They also allow tweaking the output. The syntax is rather cryptic, but it's extremely convenient. Here are a few examples:
foo*(.): only regular files matchingfoo*and symbolic links to regular files, not directories and other special files.foo*(*.): only executable regular files matchingfoo*.foo*(-.): only regular files matchingfoo*, not symbolic links and other special files.foo*(-@): only dangling symbolic links matchingfoo*.foo*(om): the files matchingfoo*, sorted by last modification date, most recent first. Note that if you pass this tols, it'll do its own sorting. This is especially useful in…foo*(om[1,10]): the 10 most recent files matchingfoo*, most recent first.foo*(Lm+1): files matchingfoo*whose size is at least 1MB.foo*(N): same asfoo*, but if this doesn't match any file, produce an empty list regardless of the setting of thenull_globoption (see above).*(D): match all files including dot files (except.and..).foo/bar/*(:t)(using a history modifier): the files infoo/bar, but with only the base name of the file. E.g. if there is afoo/bar/qux.txt, it's expanded asqux.txt.foo/bar/*(.:r): take regular files underfoo/barand remove the extension. E.g.foo/bar/qux.txtis expanded asfoo/bar/qux.foo*.odt(e''REPLY=$REPLY:r.pdf''): take the list of files matchingfoo*.odt, and replace.odtby.pdf(regardless of whether the PDF file exists).
Here are a few useful zsh-specific wildcard patterns.
foo*.txt~foobar*: all.txtfiles whose name starts withfoobut notfoobar.image<->.jpg(n): all.jpgfiles whose base name isimagefollowed by a number, e.g.image3.jpgandimage22.jpgbut notimage-backup.jpg. The glob qualifier(n)causes the files to be listed in numerical order, i.e.image9.jpgcomes beforeimage10.jpg(you can make this the default even without-nwithsetopt numeric_glob_sort).
To mass-rename files, zsh provides a very convenient tool: the zmv function. Suggested for your .zshrc:
autoload zmv
alias zcp='zmv -C' zln='zmv -L'
Example:
zmv '(*).jpeg' '$1.jpg'
zmv '(*)-backup.(*)' 'backups/$1.$2'
Bash has a few ways to apply transformations when taking the value of a variable. Zsh has some of the same and many more.
Zsh has a number of little convenient features to change directories. Turn on setopt auto_cd to change to a directory when you type its name without having to type cd (bash also has this nowadays). You can use the two-argument form to cd to change to a directory whose name is close to the current directory. For example, if you're in /some/where/foo-old/deeply/nested/inside and you want to go to /some/where/foo-new/deeply/nested/inside, just type cd old new.
To assign a value to a variable, you of course write VARIABLE=VALUE. To edit the value of a variable interactively, just run vared VARIABLE.
Final advice
Zsh comes with a configuration interface that supports a few of the most common settings, including canned recipes for things like case-insensitive completion. To (re)run this interface (the first line is not needed if you're using a configuration file that was edited by zsh-newuser-install):
autoload -U zsh-newuser-install
zsh-newuser-install
Out of the box, with no configuration file at all, many of zsh's useful features are disabled for backward compatibility with 1990's versions. zsh-newuser-install suggests some recommended features to turn on.
There are many zsh configuration frameworks on the web (many of them are on Github). They can be a convenient way to get started with some powerful features. The flip side of the coin is they often lock you in doing things the way the author does, so sometimes they'll prevent you from doing things the way you want. Use them at your own risk.
The zsh manual has a lot of information, but it's often written in a way that's terse and hard to follow, and has few examples. Don't hesitate to search for explanations and examples online: if you only use the part of zsh that's easy to understand in the manual, you'll miss out. Two good resources are the zsh-users mailing list and Unix Stack Exchange.
9
Epic answer! Thank you!
– lejonet
Jun 9 at 8:30
Now I'm wondering if the amazing ctrl-o works on zsh. Of course, it doesn't work on Mac OS on bash either, so it's not really relevant to this answer or site. I couldn't find any information on ctrl-o in zsh in a quick online search, but then again, the information on ctrl-o in bash is also universally inaccurate...
– Jasper
Jun 9 at 15:04
@Jasper I didn't know bash had this. Going by the description, C-o does the same thing in zsh with the default key bindings.
– Gilles
Jun 9 at 23:18
I was glad to see that you included the "Some nice zsh features" section, and anxiously read it to try and understand why Apple might have made the decision to switch away from the extremely-common Bash to the much less popular Zsh. I didn't find anything at all even remotely compelling there to justify the switch. It's obviously a non-exhaustive list, but were you omitting the headline features of Zsh because they're supposed to be obvious? What am I missing here?
– Cody Gray
Jun 9 at 23:58
1
@CodyGray I don't know and Apple isn't in the habit of justifying themselves. It may have had something to do with the fact that if the situation had been reversed, there wouldn't be a “nice bash features” section. Or it may be because the last non-GPLv3 bash is getting really old whereas zsh has a more liberal license.
– Gilles
Jun 10 at 0:06
|
show 3 more comments
First, some important things:
Bash isn't going away. If you're already using bash, nothing will change for you. All that changes is that zsh will be the default login shell for new accounts, and even then, you can select bash instead.
Scripts are not affected. What changes is the shell for interactive use, i.e. the shell in terminals (and also a few other things that use the login shell, such as crontabs). If you have a script in a file with execution permissions, starting with a shebang line such as#!/bin/bashor#!/bin/shor#!/usr/bin/env bash, it'll keep working exactly as before.- Zsh's syntax is not completely compatible with bash, but it's close. A lot of code will keep working, for example typical aliases and functions. The main differences are in interactive configuration features.
Now, assuming you're considering switching to zsh, which has been a possibility for years, here are the main differences you'll encounter. This is not an exhaustive list!
Main differences for interactive use
Configuration files: bash reads (mainly) .bashrc in non-login interactive shells (but macOS starts a login shell in terminals by default), .profile or .bash_profile in login shells, and .inputrc. Zsh reads (mainly) .zshrc (in all interactive shells) and .zprofile (in login shells). This means that none of your bash customizations will apply: you'll need to port them over. You can't just copy the files because many things will need tweaking.
Key bindings use completely different syntax. Bash uses .inputrc and the bind builtin to bind keys to readline commands. Zsh uses the bindkey builtin to bind keys to zle widgets. Most readline commands have a zsh equivalent, but it isn't always a perfect equivalence.
Speaking of key bindings, if you use Vi(m) as your in-terminal editor but not as your command line mode in the shell, you'll notice zsh defaults to vi editing mode if EDITOR or VISUAL is set to vi or vim. bindkey -e switches to emacs mode.
Prompt: bash sets the prompt (mainly) from PS1 which contains backslash escapes. Zsh sets the prompt mainly from PS1 which contains percent escapes. The functionality of bash's PROMPT_COMMAND is available in zsh via the precmd and preexec hook functions. Zsh has more convenience mechanisms to build fancy prompts including a prompt theme mechanism.
The basic command line history mechanisms (navigation with Up/Down, search with Ctrl+R, history expansion with !! and friends, last argument recall with Alt+. or $_) work in the same way, but there are a lot of differences in the details, too many to list here. You can copy your .bash_history to .zsh_history if you haven't changed a shell option that changes the file format.
Completion: both shells default to a basic completion mode that mostly completes command and file names, and switch to a fancy mode by including bash_completion on bash or by running compinit in zsh. You'll find some commands that bash handles better and some that zsh handles better. Zsh is usually more precise, but sometimes gives up where bash does something that isn't correct but is sensible. To specify possible completions for a command, zsh has three mechanisms:
- The “old” completion mechanism with
compctlwhich you can forget about. - The “new” completion mechanism with
compaddand lots of functions that begin with underscore and a powerful but complex user configuration mechanism. - An emulation to support bash completion functions which you can enable by running
bashcompinit. The emulation isn't 100% perfect but it usually works.
Many of bash's shopt settings have a corresponding setopt in zsh.
Zsh doesn't treat # as a comment start on the command line by default, only in scripts (including .zshrc and such). To enable interactive comments, run setopt interactive_comments.
Main differences for scripting
(and for power users on the command line of course)
In bash, $foo takes the value of foo, splits it at whitespace characters, and for each whitespace-separated part, if it contains wildcard characters and matches an existing file, replaces the pattern by the list of matches. To just get the value of foo, you need "$foo". The same applies to command substitution $(foo). In zsh, $foo is the value of foo and $(foo) is the output of foo minus its final newlines, with two exceptions. If a word becomes empty due to expanding empty unquoted variables, it's removed (e.g. a=; b=; printf "%sn" one "$a$b" three $a$b five prints one, an empty line, three, five). The result of an unquoted command substitution is split at whitespace but the pieces don't undergo wildcard matching.
Bash arrays are indexed from 0 to (length-1). Zsh arrays are indexed from 1 to length. With a=(one two three), in bash, $a[1] is two, but in zsh, it's one. In bash, if you just reference an array variable without braces, you get the first element, e.g. $a is one and $a[1] is one[1]. In zsh, $a expands to the list of non-empty elements, and $a[1] expands to the first element. Similarly, in bash, the length of an array is $#a; this also works in zsh but you can write it more simply as $#a. You can make 0-indexing the default with setopt ksh_arrays; this also turns on the requirement to use braces to refer to an array element.
Bash has extra wildcard patterns such as @(foo|bar) to match foo or bar, which are only enabled with shopt -s extglob. In zsh, you can enable these patterns with setopt ksh_glob, but there's also a simpler-to-type native syntax such as (foo|bar), some of which requires setopt extended_glob (do put that in your .zshrc, and it's on by default in completion functions). **/ for recursive directory traversal is always enabled in zsh.
In bash, by default, if a wildcard pattern doesn't match any file, it's left unchanged. In zsh, by default, you'll get an error, which is usually the safest setting. If you want to pass a wildcard parameter to a command, use quotes. You can switch to the bash behavior with setopt no_nomatch. You can make non-matching wildcard patterns expand to an empty list instead with setopt null_glob.
In bash, the right-hand side of a pipeline runs in a subshell. In zsh, it runs in the parent shell, so you can write things like somecommand | read output.
Some nice zsh features
Here are a few nice zsh features that bash doesn't have (at least not without some serious elbow grease). Once again, this is just a selection of the ones I consider the most useful.
Glob qualifiers allow matching files based on metadata such as their time stamp, their size, etc. They also allow tweaking the output. The syntax is rather cryptic, but it's extremely convenient. Here are a few examples:
foo*(.): only regular files matchingfoo*and symbolic links to regular files, not directories and other special files.foo*(*.): only executable regular files matchingfoo*.foo*(-.): only regular files matchingfoo*, not symbolic links and other special files.foo*(-@): only dangling symbolic links matchingfoo*.foo*(om): the files matchingfoo*, sorted by last modification date, most recent first. Note that if you pass this tols, it'll do its own sorting. This is especially useful in…foo*(om[1,10]): the 10 most recent files matchingfoo*, most recent first.foo*(Lm+1): files matchingfoo*whose size is at least 1MB.foo*(N): same asfoo*, but if this doesn't match any file, produce an empty list regardless of the setting of thenull_globoption (see above).*(D): match all files including dot files (except.and..).foo/bar/*(:t)(using a history modifier): the files infoo/bar, but with only the base name of the file. E.g. if there is afoo/bar/qux.txt, it's expanded asqux.txt.foo/bar/*(.:r): take regular files underfoo/barand remove the extension. E.g.foo/bar/qux.txtis expanded asfoo/bar/qux.foo*.odt(e''REPLY=$REPLY:r.pdf''): take the list of files matchingfoo*.odt, and replace.odtby.pdf(regardless of whether the PDF file exists).
Here are a few useful zsh-specific wildcard patterns.
foo*.txt~foobar*: all.txtfiles whose name starts withfoobut notfoobar.image<->.jpg(n): all.jpgfiles whose base name isimagefollowed by a number, e.g.image3.jpgandimage22.jpgbut notimage-backup.jpg. The glob qualifier(n)causes the files to be listed in numerical order, i.e.image9.jpgcomes beforeimage10.jpg(you can make this the default even without-nwithsetopt numeric_glob_sort).
To mass-rename files, zsh provides a very convenient tool: the zmv function. Suggested for your .zshrc:
autoload zmv
alias zcp='zmv -C' zln='zmv -L'
Example:
zmv '(*).jpeg' '$1.jpg'
zmv '(*)-backup.(*)' 'backups/$1.$2'
Bash has a few ways to apply transformations when taking the value of a variable. Zsh has some of the same and many more.
Zsh has a number of little convenient features to change directories. Turn on setopt auto_cd to change to a directory when you type its name without having to type cd (bash also has this nowadays). You can use the two-argument form to cd to change to a directory whose name is close to the current directory. For example, if you're in /some/where/foo-old/deeply/nested/inside and you want to go to /some/where/foo-new/deeply/nested/inside, just type cd old new.
To assign a value to a variable, you of course write VARIABLE=VALUE. To edit the value of a variable interactively, just run vared VARIABLE.
Final advice
Zsh comes with a configuration interface that supports a few of the most common settings, including canned recipes for things like case-insensitive completion. To (re)run this interface (the first line is not needed if you're using a configuration file that was edited by zsh-newuser-install):
autoload -U zsh-newuser-install
zsh-newuser-install
Out of the box, with no configuration file at all, many of zsh's useful features are disabled for backward compatibility with 1990's versions. zsh-newuser-install suggests some recommended features to turn on.
There are many zsh configuration frameworks on the web (many of them are on Github). They can be a convenient way to get started with some powerful features. The flip side of the coin is they often lock you in doing things the way the author does, so sometimes they'll prevent you from doing things the way you want. Use them at your own risk.
The zsh manual has a lot of information, but it's often written in a way that's terse and hard to follow, and has few examples. Don't hesitate to search for explanations and examples online: if you only use the part of zsh that's easy to understand in the manual, you'll miss out. Two good resources are the zsh-users mailing list and Unix Stack Exchange.
First, some important things:
Bash isn't going away. If you're already using bash, nothing will change for you. All that changes is that zsh will be the default login shell for new accounts, and even then, you can select bash instead.
Scripts are not affected. What changes is the shell for interactive use, i.e. the shell in terminals (and also a few other things that use the login shell, such as crontabs). If you have a script in a file with execution permissions, starting with a shebang line such as#!/bin/bashor#!/bin/shor#!/usr/bin/env bash, it'll keep working exactly as before.- Zsh's syntax is not completely compatible with bash, but it's close. A lot of code will keep working, for example typical aliases and functions. The main differences are in interactive configuration features.
Now, assuming you're considering switching to zsh, which has been a possibility for years, here are the main differences you'll encounter. This is not an exhaustive list!
Main differences for interactive use
Configuration files: bash reads (mainly) .bashrc in non-login interactive shells (but macOS starts a login shell in terminals by default), .profile or .bash_profile in login shells, and .inputrc. Zsh reads (mainly) .zshrc (in all interactive shells) and .zprofile (in login shells). This means that none of your bash customizations will apply: you'll need to port them over. You can't just copy the files because many things will need tweaking.
Key bindings use completely different syntax. Bash uses .inputrc and the bind builtin to bind keys to readline commands. Zsh uses the bindkey builtin to bind keys to zle widgets. Most readline commands have a zsh equivalent, but it isn't always a perfect equivalence.
Speaking of key bindings, if you use Vi(m) as your in-terminal editor but not as your command line mode in the shell, you'll notice zsh defaults to vi editing mode if EDITOR or VISUAL is set to vi or vim. bindkey -e switches to emacs mode.
Prompt: bash sets the prompt (mainly) from PS1 which contains backslash escapes. Zsh sets the prompt mainly from PS1 which contains percent escapes. The functionality of bash's PROMPT_COMMAND is available in zsh via the precmd and preexec hook functions. Zsh has more convenience mechanisms to build fancy prompts including a prompt theme mechanism.
The basic command line history mechanisms (navigation with Up/Down, search with Ctrl+R, history expansion with !! and friends, last argument recall with Alt+. or $_) work in the same way, but there are a lot of differences in the details, too many to list here. You can copy your .bash_history to .zsh_history if you haven't changed a shell option that changes the file format.
Completion: both shells default to a basic completion mode that mostly completes command and file names, and switch to a fancy mode by including bash_completion on bash or by running compinit in zsh. You'll find some commands that bash handles better and some that zsh handles better. Zsh is usually more precise, but sometimes gives up where bash does something that isn't correct but is sensible. To specify possible completions for a command, zsh has three mechanisms:
- The “old” completion mechanism with
compctlwhich you can forget about. - The “new” completion mechanism with
compaddand lots of functions that begin with underscore and a powerful but complex user configuration mechanism. - An emulation to support bash completion functions which you can enable by running
bashcompinit. The emulation isn't 100% perfect but it usually works.
Many of bash's shopt settings have a corresponding setopt in zsh.
Zsh doesn't treat # as a comment start on the command line by default, only in scripts (including .zshrc and such). To enable interactive comments, run setopt interactive_comments.
Main differences for scripting
(and for power users on the command line of course)
In bash, $foo takes the value of foo, splits it at whitespace characters, and for each whitespace-separated part, if it contains wildcard characters and matches an existing file, replaces the pattern by the list of matches. To just get the value of foo, you need "$foo". The same applies to command substitution $(foo). In zsh, $foo is the value of foo and $(foo) is the output of foo minus its final newlines, with two exceptions. If a word becomes empty due to expanding empty unquoted variables, it's removed (e.g. a=; b=; printf "%sn" one "$a$b" three $a$b five prints one, an empty line, three, five). The result of an unquoted command substitution is split at whitespace but the pieces don't undergo wildcard matching.
Bash arrays are indexed from 0 to (length-1). Zsh arrays are indexed from 1 to length. With a=(one two three), in bash, $a[1] is two, but in zsh, it's one. In bash, if you just reference an array variable without braces, you get the first element, e.g. $a is one and $a[1] is one[1]. In zsh, $a expands to the list of non-empty elements, and $a[1] expands to the first element. Similarly, in bash, the length of an array is $#a; this also works in zsh but you can write it more simply as $#a. You can make 0-indexing the default with setopt ksh_arrays; this also turns on the requirement to use braces to refer to an array element.
Bash has extra wildcard patterns such as @(foo|bar) to match foo or bar, which are only enabled with shopt -s extglob. In zsh, you can enable these patterns with setopt ksh_glob, but there's also a simpler-to-type native syntax such as (foo|bar), some of which requires setopt extended_glob (do put that in your .zshrc, and it's on by default in completion functions). **/ for recursive directory traversal is always enabled in zsh.
In bash, by default, if a wildcard pattern doesn't match any file, it's left unchanged. In zsh, by default, you'll get an error, which is usually the safest setting. If you want to pass a wildcard parameter to a command, use quotes. You can switch to the bash behavior with setopt no_nomatch. You can make non-matching wildcard patterns expand to an empty list instead with setopt null_glob.
In bash, the right-hand side of a pipeline runs in a subshell. In zsh, it runs in the parent shell, so you can write things like somecommand | read output.
Some nice zsh features
Here are a few nice zsh features that bash doesn't have (at least not without some serious elbow grease). Once again, this is just a selection of the ones I consider the most useful.
Glob qualifiers allow matching files based on metadata such as their time stamp, their size, etc. They also allow tweaking the output. The syntax is rather cryptic, but it's extremely convenient. Here are a few examples:
foo*(.): only regular files matchingfoo*and symbolic links to regular files, not directories and other special files.foo*(*.): only executable regular files matchingfoo*.foo*(-.): only regular files matchingfoo*, not symbolic links and other special files.foo*(-@): only dangling symbolic links matchingfoo*.foo*(om): the files matchingfoo*, sorted by last modification date, most recent first. Note that if you pass this tols, it'll do its own sorting. This is especially useful in…foo*(om[1,10]): the 10 most recent files matchingfoo*, most recent first.foo*(Lm+1): files matchingfoo*whose size is at least 1MB.foo*(N): same asfoo*, but if this doesn't match any file, produce an empty list regardless of the setting of thenull_globoption (see above).*(D): match all files including dot files (except.and..).foo/bar/*(:t)(using a history modifier): the files infoo/bar, but with only the base name of the file. E.g. if there is afoo/bar/qux.txt, it's expanded asqux.txt.foo/bar/*(.:r): take regular files underfoo/barand remove the extension. E.g.foo/bar/qux.txtis expanded asfoo/bar/qux.foo*.odt(e''REPLY=$REPLY:r.pdf''): take the list of files matchingfoo*.odt, and replace.odtby.pdf(regardless of whether the PDF file exists).
Here are a few useful zsh-specific wildcard patterns.
foo*.txt~foobar*: all.txtfiles whose name starts withfoobut notfoobar.image<->.jpg(n): all.jpgfiles whose base name isimagefollowed by a number, e.g.image3.jpgandimage22.jpgbut notimage-backup.jpg. The glob qualifier(n)causes the files to be listed in numerical order, i.e.image9.jpgcomes beforeimage10.jpg(you can make this the default even without-nwithsetopt numeric_glob_sort).
To mass-rename files, zsh provides a very convenient tool: the zmv function. Suggested for your .zshrc:
autoload zmv
alias zcp='zmv -C' zln='zmv -L'
Example:
zmv '(*).jpeg' '$1.jpg'
zmv '(*)-backup.(*)' 'backups/$1.$2'
Bash has a few ways to apply transformations when taking the value of a variable. Zsh has some of the same and many more.
Zsh has a number of little convenient features to change directories. Turn on setopt auto_cd to change to a directory when you type its name without having to type cd (bash also has this nowadays). You can use the two-argument form to cd to change to a directory whose name is close to the current directory. For example, if you're in /some/where/foo-old/deeply/nested/inside and you want to go to /some/where/foo-new/deeply/nested/inside, just type cd old new.
To assign a value to a variable, you of course write VARIABLE=VALUE. To edit the value of a variable interactively, just run vared VARIABLE.
Final advice
Zsh comes with a configuration interface that supports a few of the most common settings, including canned recipes for things like case-insensitive completion. To (re)run this interface (the first line is not needed if you're using a configuration file that was edited by zsh-newuser-install):
autoload -U zsh-newuser-install
zsh-newuser-install
Out of the box, with no configuration file at all, many of zsh's useful features are disabled for backward compatibility with 1990's versions. zsh-newuser-install suggests some recommended features to turn on.
There are many zsh configuration frameworks on the web (many of them are on Github). They can be a convenient way to get started with some powerful features. The flip side of the coin is they often lock you in doing things the way the author does, so sometimes they'll prevent you from doing things the way you want. Use them at your own risk.
The zsh manual has a lot of information, but it's often written in a way that's terse and hard to follow, and has few examples. Don't hesitate to search for explanations and examples online: if you only use the part of zsh that's easy to understand in the manual, you'll miss out. Two good resources are the zsh-users mailing list and Unix Stack Exchange.
edited Jun 12 at 19:20
answered Jun 8 at 23:45
GillesGilles
3,9861 gold badge17 silver badges24 bronze badges
3,9861 gold badge17 silver badges24 bronze badges
9
Epic answer! Thank you!
– lejonet
Jun 9 at 8:30
Now I'm wondering if the amazing ctrl-o works on zsh. Of course, it doesn't work on Mac OS on bash either, so it's not really relevant to this answer or site. I couldn't find any information on ctrl-o in zsh in a quick online search, but then again, the information on ctrl-o in bash is also universally inaccurate...
– Jasper
Jun 9 at 15:04
@Jasper I didn't know bash had this. Going by the description, C-o does the same thing in zsh with the default key bindings.
– Gilles
Jun 9 at 23:18
I was glad to see that you included the "Some nice zsh features" section, and anxiously read it to try and understand why Apple might have made the decision to switch away from the extremely-common Bash to the much less popular Zsh. I didn't find anything at all even remotely compelling there to justify the switch. It's obviously a non-exhaustive list, but were you omitting the headline features of Zsh because they're supposed to be obvious? What am I missing here?
– Cody Gray
Jun 9 at 23:58
1
@CodyGray I don't know and Apple isn't in the habit of justifying themselves. It may have had something to do with the fact that if the situation had been reversed, there wouldn't be a “nice bash features” section. Or it may be because the last non-GPLv3 bash is getting really old whereas zsh has a more liberal license.
– Gilles
Jun 10 at 0:06
|
show 3 more comments
9
Epic answer! Thank you!
– lejonet
Jun 9 at 8:30
Now I'm wondering if the amazing ctrl-o works on zsh. Of course, it doesn't work on Mac OS on bash either, so it's not really relevant to this answer or site. I couldn't find any information on ctrl-o in zsh in a quick online search, but then again, the information on ctrl-o in bash is also universally inaccurate...
– Jasper
Jun 9 at 15:04
@Jasper I didn't know bash had this. Going by the description, C-o does the same thing in zsh with the default key bindings.
– Gilles
Jun 9 at 23:18
I was glad to see that you included the "Some nice zsh features" section, and anxiously read it to try and understand why Apple might have made the decision to switch away from the extremely-common Bash to the much less popular Zsh. I didn't find anything at all even remotely compelling there to justify the switch. It's obviously a non-exhaustive list, but were you omitting the headline features of Zsh because they're supposed to be obvious? What am I missing here?
– Cody Gray
Jun 9 at 23:58
1
@CodyGray I don't know and Apple isn't in the habit of justifying themselves. It may have had something to do with the fact that if the situation had been reversed, there wouldn't be a “nice bash features” section. Or it may be because the last non-GPLv3 bash is getting really old whereas zsh has a more liberal license.
– Gilles
Jun 10 at 0:06
9
9
Epic answer! Thank you!
– lejonet
Jun 9 at 8:30
Epic answer! Thank you!
– lejonet
Jun 9 at 8:30
Now I'm wondering if the amazing ctrl-o works on zsh. Of course, it doesn't work on Mac OS on bash either, so it's not really relevant to this answer or site. I couldn't find any information on ctrl-o in zsh in a quick online search, but then again, the information on ctrl-o in bash is also universally inaccurate...
– Jasper
Jun 9 at 15:04
Now I'm wondering if the amazing ctrl-o works on zsh. Of course, it doesn't work on Mac OS on bash either, so it's not really relevant to this answer or site. I couldn't find any information on ctrl-o in zsh in a quick online search, but then again, the information on ctrl-o in bash is also universally inaccurate...
– Jasper
Jun 9 at 15:04
@Jasper I didn't know bash had this. Going by the description, C-o does the same thing in zsh with the default key bindings.
– Gilles
Jun 9 at 23:18
@Jasper I didn't know bash had this. Going by the description, C-o does the same thing in zsh with the default key bindings.
– Gilles
Jun 9 at 23:18
I was glad to see that you included the "Some nice zsh features" section, and anxiously read it to try and understand why Apple might have made the decision to switch away from the extremely-common Bash to the much less popular Zsh. I didn't find anything at all even remotely compelling there to justify the switch. It's obviously a non-exhaustive list, but were you omitting the headline features of Zsh because they're supposed to be obvious? What am I missing here?
– Cody Gray
Jun 9 at 23:58
I was glad to see that you included the "Some nice zsh features" section, and anxiously read it to try and understand why Apple might have made the decision to switch away from the extremely-common Bash to the much less popular Zsh. I didn't find anything at all even remotely compelling there to justify the switch. It's obviously a non-exhaustive list, but were you omitting the headline features of Zsh because they're supposed to be obvious? What am I missing here?
– Cody Gray
Jun 9 at 23:58
1
1
@CodyGray I don't know and Apple isn't in the habit of justifying themselves. It may have had something to do with the fact that if the situation had been reversed, there wouldn't be a “nice bash features” section. Or it may be because the last non-GPLv3 bash is getting really old whereas zsh has a more liberal license.
– Gilles
Jun 10 at 0:06
@CodyGray I don't know and Apple isn't in the habit of justifying themselves. It may have had something to do with the fact that if the situation had been reversed, there wouldn't be a “nice bash features” section. Or it may be because the last non-GPLv3 bash is getting really old whereas zsh has a more liberal license.
– Gilles
Jun 10 at 0:06
|
show 3 more comments
Change your shell now and test - no need to wait.
chsh -s /bin/zsh
- All the scripts that depend on
bashsyntax will still find and call bash. - the same bash from Mojave is shipping on Catalina and migrated users keep their old shell.
Also, I would estimate 95% of macOS users don't use a command line and of those that do, another 95% won't have to change anything significant or at all. (I'd wager it's more like 10% of the 1% that know shells exist need to do anything other than port a couple lines in their .dot files)
Your prompt will change and if you changed your prompt on bash, the way to change it on zsh is no harder and no less documented than bash.
The newer shells would fail to ever get off the ground if they broke major items or caused a painful adaptation period. If you want a more fundamental change and really want a shell you need to think about and requires training and intention to adopt - try fish.
I am conflicted withfish. I use it for two years now but the incompatibility of some copy/pasted one-lines is tiring. On the other hand it is a very handy shell (the automatic suggestions without <kbd>Tab</kbd> are excellent)
– WoJ
Jun 8 at 10:08
I wanted to love fish, I still want to love fish, but I have too many shell constructs from a decade inkshto ever really leave that fold. I’ll gladly leave bash behind and fully embrace zsh now, personally.
– bmike♦
Jun 8 at 14:33
1
I am thoroughly underwhelmed with fish. It is really just yet another Unix-like shell with a little bit less cruft. (It literally says "Finally, a command line shell for the 90s" on the homepage, after all.) The only new shell that I have seen in recent years that actually did bring something new to the (mainstream) table, was PowerShell, which unfortunately was confined to Windows much too long, and is still confined to .NET. There still is not that much new in PowerShell that hasn't already been done e.g. in DCL or JCL, but it has been done in a (somewhat) tasteful way.
– Jörg W Mittag
Jun 8 at 14:45
@bmike Why not just use ksh, then? Apple ships the most current version. Myself, I punted bash long ago and see no reason to start using zsh now.
– Marc Wilson
Jun 8 at 23:46
4
This answer doesn't describe the differences between bash and zsh at all though... and I'm not quite sure why specifying that "95% of macOS users don't use command line" is relevant for a question of a user which obviously does use bash.
– Mavrik
Jun 9 at 9:05
|
show 1 more comment
Change your shell now and test - no need to wait.
chsh -s /bin/zsh
- All the scripts that depend on
bashsyntax will still find and call bash. - the same bash from Mojave is shipping on Catalina and migrated users keep their old shell.
Also, I would estimate 95% of macOS users don't use a command line and of those that do, another 95% won't have to change anything significant or at all. (I'd wager it's more like 10% of the 1% that know shells exist need to do anything other than port a couple lines in their .dot files)
Your prompt will change and if you changed your prompt on bash, the way to change it on zsh is no harder and no less documented than bash.
The newer shells would fail to ever get off the ground if they broke major items or caused a painful adaptation period. If you want a more fundamental change and really want a shell you need to think about and requires training and intention to adopt - try fish.
I am conflicted withfish. I use it for two years now but the incompatibility of some copy/pasted one-lines is tiring. On the other hand it is a very handy shell (the automatic suggestions without <kbd>Tab</kbd> are excellent)
– WoJ
Jun 8 at 10:08
I wanted to love fish, I still want to love fish, but I have too many shell constructs from a decade inkshto ever really leave that fold. I’ll gladly leave bash behind and fully embrace zsh now, personally.
– bmike♦
Jun 8 at 14:33
1
I am thoroughly underwhelmed with fish. It is really just yet another Unix-like shell with a little bit less cruft. (It literally says "Finally, a command line shell for the 90s" on the homepage, after all.) The only new shell that I have seen in recent years that actually did bring something new to the (mainstream) table, was PowerShell, which unfortunately was confined to Windows much too long, and is still confined to .NET. There still is not that much new in PowerShell that hasn't already been done e.g. in DCL or JCL, but it has been done in a (somewhat) tasteful way.
– Jörg W Mittag
Jun 8 at 14:45
@bmike Why not just use ksh, then? Apple ships the most current version. Myself, I punted bash long ago and see no reason to start using zsh now.
– Marc Wilson
Jun 8 at 23:46
4
This answer doesn't describe the differences between bash and zsh at all though... and I'm not quite sure why specifying that "95% of macOS users don't use command line" is relevant for a question of a user which obviously does use bash.
– Mavrik
Jun 9 at 9:05
|
show 1 more comment
Change your shell now and test - no need to wait.
chsh -s /bin/zsh
- All the scripts that depend on
bashsyntax will still find and call bash. - the same bash from Mojave is shipping on Catalina and migrated users keep their old shell.
Also, I would estimate 95% of macOS users don't use a command line and of those that do, another 95% won't have to change anything significant or at all. (I'd wager it's more like 10% of the 1% that know shells exist need to do anything other than port a couple lines in their .dot files)
Your prompt will change and if you changed your prompt on bash, the way to change it on zsh is no harder and no less documented than bash.
The newer shells would fail to ever get off the ground if they broke major items or caused a painful adaptation period. If you want a more fundamental change and really want a shell you need to think about and requires training and intention to adopt - try fish.
Change your shell now and test - no need to wait.
chsh -s /bin/zsh
- All the scripts that depend on
bashsyntax will still find and call bash. - the same bash from Mojave is shipping on Catalina and migrated users keep their old shell.
Also, I would estimate 95% of macOS users don't use a command line and of those that do, another 95% won't have to change anything significant or at all. (I'd wager it's more like 10% of the 1% that know shells exist need to do anything other than port a couple lines in their .dot files)
Your prompt will change and if you changed your prompt on bash, the way to change it on zsh is no harder and no less documented than bash.
The newer shells would fail to ever get off the ground if they broke major items or caused a painful adaptation period. If you want a more fundamental change and really want a shell you need to think about and requires training and intention to adopt - try fish.
edited Jun 8 at 14:35
answered Jun 7 at 22:04
bmike♦bmike
170k47 gold badges311 silver badges670 bronze badges
170k47 gold badges311 silver badges670 bronze badges
I am conflicted withfish. I use it for two years now but the incompatibility of some copy/pasted one-lines is tiring. On the other hand it is a very handy shell (the automatic suggestions without <kbd>Tab</kbd> are excellent)
– WoJ
Jun 8 at 10:08
I wanted to love fish, I still want to love fish, but I have too many shell constructs from a decade inkshto ever really leave that fold. I’ll gladly leave bash behind and fully embrace zsh now, personally.
– bmike♦
Jun 8 at 14:33
1
I am thoroughly underwhelmed with fish. It is really just yet another Unix-like shell with a little bit less cruft. (It literally says "Finally, a command line shell for the 90s" on the homepage, after all.) The only new shell that I have seen in recent years that actually did bring something new to the (mainstream) table, was PowerShell, which unfortunately was confined to Windows much too long, and is still confined to .NET. There still is not that much new in PowerShell that hasn't already been done e.g. in DCL or JCL, but it has been done in a (somewhat) tasteful way.
– Jörg W Mittag
Jun 8 at 14:45
@bmike Why not just use ksh, then? Apple ships the most current version. Myself, I punted bash long ago and see no reason to start using zsh now.
– Marc Wilson
Jun 8 at 23:46
4
This answer doesn't describe the differences between bash and zsh at all though... and I'm not quite sure why specifying that "95% of macOS users don't use command line" is relevant for a question of a user which obviously does use bash.
– Mavrik
Jun 9 at 9:05
|
show 1 more comment
I am conflicted withfish. I use it for two years now but the incompatibility of some copy/pasted one-lines is tiring. On the other hand it is a very handy shell (the automatic suggestions without <kbd>Tab</kbd> are excellent)
– WoJ
Jun 8 at 10:08
I wanted to love fish, I still want to love fish, but I have too many shell constructs from a decade inkshto ever really leave that fold. I’ll gladly leave bash behind and fully embrace zsh now, personally.
– bmike♦
Jun 8 at 14:33
1
I am thoroughly underwhelmed with fish. It is really just yet another Unix-like shell with a little bit less cruft. (It literally says "Finally, a command line shell for the 90s" on the homepage, after all.) The only new shell that I have seen in recent years that actually did bring something new to the (mainstream) table, was PowerShell, which unfortunately was confined to Windows much too long, and is still confined to .NET. There still is not that much new in PowerShell that hasn't already been done e.g. in DCL or JCL, but it has been done in a (somewhat) tasteful way.
– Jörg W Mittag
Jun 8 at 14:45
@bmike Why not just use ksh, then? Apple ships the most current version. Myself, I punted bash long ago and see no reason to start using zsh now.
– Marc Wilson
Jun 8 at 23:46
4
This answer doesn't describe the differences between bash and zsh at all though... and I'm not quite sure why specifying that "95% of macOS users don't use command line" is relevant for a question of a user which obviously does use bash.
– Mavrik
Jun 9 at 9:05
I am conflicted with
fish. I use it for two years now but the incompatibility of some copy/pasted one-lines is tiring. On the other hand it is a very handy shell (the automatic suggestions without <kbd>Tab</kbd> are excellent)– WoJ
Jun 8 at 10:08
I am conflicted with
fish. I use it for two years now but the incompatibility of some copy/pasted one-lines is tiring. On the other hand it is a very handy shell (the automatic suggestions without <kbd>Tab</kbd> are excellent)– WoJ
Jun 8 at 10:08
I wanted to love fish, I still want to love fish, but I have too many shell constructs from a decade in
ksh to ever really leave that fold. I’ll gladly leave bash behind and fully embrace zsh now, personally.– bmike♦
Jun 8 at 14:33
I wanted to love fish, I still want to love fish, but I have too many shell constructs from a decade in
ksh to ever really leave that fold. I’ll gladly leave bash behind and fully embrace zsh now, personally.– bmike♦
Jun 8 at 14:33
1
1
I am thoroughly underwhelmed with fish. It is really just yet another Unix-like shell with a little bit less cruft. (It literally says "Finally, a command line shell for the 90s" on the homepage, after all.) The only new shell that I have seen in recent years that actually did bring something new to the (mainstream) table, was PowerShell, which unfortunately was confined to Windows much too long, and is still confined to .NET. There still is not that much new in PowerShell that hasn't already been done e.g. in DCL or JCL, but it has been done in a (somewhat) tasteful way.
– Jörg W Mittag
Jun 8 at 14:45
I am thoroughly underwhelmed with fish. It is really just yet another Unix-like shell with a little bit less cruft. (It literally says "Finally, a command line shell for the 90s" on the homepage, after all.) The only new shell that I have seen in recent years that actually did bring something new to the (mainstream) table, was PowerShell, which unfortunately was confined to Windows much too long, and is still confined to .NET. There still is not that much new in PowerShell that hasn't already been done e.g. in DCL or JCL, but it has been done in a (somewhat) tasteful way.
– Jörg W Mittag
Jun 8 at 14:45
@bmike Why not just use ksh, then? Apple ships the most current version. Myself, I punted bash long ago and see no reason to start using zsh now.
– Marc Wilson
Jun 8 at 23:46
@bmike Why not just use ksh, then? Apple ships the most current version. Myself, I punted bash long ago and see no reason to start using zsh now.
– Marc Wilson
Jun 8 at 23:46
4
4
This answer doesn't describe the differences between bash and zsh at all though... and I'm not quite sure why specifying that "95% of macOS users don't use command line" is relevant for a question of a user which obviously does use bash.
– Mavrik
Jun 9 at 9:05
This answer doesn't describe the differences between bash and zsh at all though... and I'm not quite sure why specifying that "95% of macOS users don't use command line" is relevant for a question of a user which obviously does use bash.
– Mavrik
Jun 9 at 9:05
|
show 1 more comment
My shell scripts are really not that complicated
Do your shell scripts have shebang lines (begin with #! /bin/bash or similar)? If not, you might have unintentionally been using a bash feature, where it runs scripts without a shebang using bash. Other shells, like dash or zsh, leave it up to the OS, which would usually use /bin/sh instead. /bin/sh on macOS is, and probably will remain, a copy of /bin/bash, but executing bash with the name sh causes it to have different behaviour.
The specifics are the Bash manual, 6.11 Bash POSIX mode. Some points:
- Bash ensures that the
POSIXLY_CORRECTvariable is set.
This environment variable may affect the behaviour of a number of other tools, especially if you have GNU tools installed.
- Process substitution is not available.
Process substitution is the <(...) or >(...) syntax.
- The
.andsourcebuiltins do not search the current directory for the filename argument if it is not found by searching PATH.
So if your script did . foo expecting it to source a file named foo in the current directory, that won't work. You should do . ./foo, instead.
As you can guess from the numbers, there are a lot of minor differences in behaviour of bash in POSIX mode. Best use a shebang if you mean to use bash for your scripts.
1
In checking it looks like the vast majority of the shell scripts I wrote or use either explicitly state /bin/bash in the shebang (very few of them) or state /bin/sh (nearly all of them), so that at least should not be an issue. Thanks.
– dr.nixon
Jun 10 at 21:25
add a comment
|
My shell scripts are really not that complicated
Do your shell scripts have shebang lines (begin with #! /bin/bash or similar)? If not, you might have unintentionally been using a bash feature, where it runs scripts without a shebang using bash. Other shells, like dash or zsh, leave it up to the OS, which would usually use /bin/sh instead. /bin/sh on macOS is, and probably will remain, a copy of /bin/bash, but executing bash with the name sh causes it to have different behaviour.
The specifics are the Bash manual, 6.11 Bash POSIX mode. Some points:
- Bash ensures that the
POSIXLY_CORRECTvariable is set.
This environment variable may affect the behaviour of a number of other tools, especially if you have GNU tools installed.
- Process substitution is not available.
Process substitution is the <(...) or >(...) syntax.
- The
.andsourcebuiltins do not search the current directory for the filename argument if it is not found by searching PATH.
So if your script did . foo expecting it to source a file named foo in the current directory, that won't work. You should do . ./foo, instead.
As you can guess from the numbers, there are a lot of minor differences in behaviour of bash in POSIX mode. Best use a shebang if you mean to use bash for your scripts.
1
In checking it looks like the vast majority of the shell scripts I wrote or use either explicitly state /bin/bash in the shebang (very few of them) or state /bin/sh (nearly all of them), so that at least should not be an issue. Thanks.
– dr.nixon
Jun 10 at 21:25
add a comment
|
My shell scripts are really not that complicated
Do your shell scripts have shebang lines (begin with #! /bin/bash or similar)? If not, you might have unintentionally been using a bash feature, where it runs scripts without a shebang using bash. Other shells, like dash or zsh, leave it up to the OS, which would usually use /bin/sh instead. /bin/sh on macOS is, and probably will remain, a copy of /bin/bash, but executing bash with the name sh causes it to have different behaviour.
The specifics are the Bash manual, 6.11 Bash POSIX mode. Some points:
- Bash ensures that the
POSIXLY_CORRECTvariable is set.
This environment variable may affect the behaviour of a number of other tools, especially if you have GNU tools installed.
- Process substitution is not available.
Process substitution is the <(...) or >(...) syntax.
- The
.andsourcebuiltins do not search the current directory for the filename argument if it is not found by searching PATH.
So if your script did . foo expecting it to source a file named foo in the current directory, that won't work. You should do . ./foo, instead.
As you can guess from the numbers, there are a lot of minor differences in behaviour of bash in POSIX mode. Best use a shebang if you mean to use bash for your scripts.
My shell scripts are really not that complicated
Do your shell scripts have shebang lines (begin with #! /bin/bash or similar)? If not, you might have unintentionally been using a bash feature, where it runs scripts without a shebang using bash. Other shells, like dash or zsh, leave it up to the OS, which would usually use /bin/sh instead. /bin/sh on macOS is, and probably will remain, a copy of /bin/bash, but executing bash with the name sh causes it to have different behaviour.
The specifics are the Bash manual, 6.11 Bash POSIX mode. Some points:
- Bash ensures that the
POSIXLY_CORRECTvariable is set.
This environment variable may affect the behaviour of a number of other tools, especially if you have GNU tools installed.
- Process substitution is not available.
Process substitution is the <(...) or >(...) syntax.
- The
.andsourcebuiltins do not search the current directory for the filename argument if it is not found by searching PATH.
So if your script did . foo expecting it to source a file named foo in the current directory, that won't work. You should do . ./foo, instead.
As you can guess from the numbers, there are a lot of minor differences in behaviour of bash in POSIX mode. Best use a shebang if you mean to use bash for your scripts.
answered Jun 8 at 7:16
murumuru
6954 silver badges15 bronze badges
6954 silver badges15 bronze badges
1
In checking it looks like the vast majority of the shell scripts I wrote or use either explicitly state /bin/bash in the shebang (very few of them) or state /bin/sh (nearly all of them), so that at least should not be an issue. Thanks.
– dr.nixon
Jun 10 at 21:25
add a comment
|
1
In checking it looks like the vast majority of the shell scripts I wrote or use either explicitly state /bin/bash in the shebang (very few of them) or state /bin/sh (nearly all of them), so that at least should not be an issue. Thanks.
– dr.nixon
Jun 10 at 21:25
1
1
In checking it looks like the vast majority of the shell scripts I wrote or use either explicitly state /bin/bash in the shebang (very few of them) or state /bin/sh (nearly all of them), so that at least should not be an issue. Thanks.
– dr.nixon
Jun 10 at 21:25
In checking it looks like the vast majority of the shell scripts I wrote or use either explicitly state /bin/bash in the shebang (very few of them) or state /bin/sh (nearly all of them), so that at least should not be an issue. Thanks.
– dr.nixon
Jun 10 at 21:25
add a comment
|
In the spirit of keeping things simple...
Can anyone provide a simple practical comparison, or specific stumbling blocks I will need to know, so that I can start working towards being ready for the new shell when Catalina is released?
If you're thinking using the new default shell consider:
- If you want to give Zsh a whirl and feel some of the differences without changing shell settings on your machine you might try Powerline10k in a Docker container and see if it is your cup of tea.
- If you don't need all the bells and whistles and use Bash for just basic scripts it's rather easy to set your shell as others here have explained. And if you decide you want to use features in Bash 5 it's a fairly trivial upgrade for macOS.
- If you want to improve the portability of your scripts so they're more likely to function as expected in both shells, test them for POSIX-compliance and remove any "bashisms". I've used ShellCheck for this and it works quite well for less complicated scripts.
While no particular path is clear these three approaches should give you enough confidence to make an informed decision without over engineering the problem or solution space.
add a comment
|
In the spirit of keeping things simple...
Can anyone provide a simple practical comparison, or specific stumbling blocks I will need to know, so that I can start working towards being ready for the new shell when Catalina is released?
If you're thinking using the new default shell consider:
- If you want to give Zsh a whirl and feel some of the differences without changing shell settings on your machine you might try Powerline10k in a Docker container and see if it is your cup of tea.
- If you don't need all the bells and whistles and use Bash for just basic scripts it's rather easy to set your shell as others here have explained. And if you decide you want to use features in Bash 5 it's a fairly trivial upgrade for macOS.
- If you want to improve the portability of your scripts so they're more likely to function as expected in both shells, test them for POSIX-compliance and remove any "bashisms". I've used ShellCheck for this and it works quite well for less complicated scripts.
While no particular path is clear these three approaches should give you enough confidence to make an informed decision without over engineering the problem or solution space.
add a comment
|
In the spirit of keeping things simple...
Can anyone provide a simple practical comparison, or specific stumbling blocks I will need to know, so that I can start working towards being ready for the new shell when Catalina is released?
If you're thinking using the new default shell consider:
- If you want to give Zsh a whirl and feel some of the differences without changing shell settings on your machine you might try Powerline10k in a Docker container and see if it is your cup of tea.
- If you don't need all the bells and whistles and use Bash for just basic scripts it's rather easy to set your shell as others here have explained. And if you decide you want to use features in Bash 5 it's a fairly trivial upgrade for macOS.
- If you want to improve the portability of your scripts so they're more likely to function as expected in both shells, test them for POSIX-compliance and remove any "bashisms". I've used ShellCheck for this and it works quite well for less complicated scripts.
While no particular path is clear these three approaches should give you enough confidence to make an informed decision without over engineering the problem or solution space.
In the spirit of keeping things simple...
Can anyone provide a simple practical comparison, or specific stumbling blocks I will need to know, so that I can start working towards being ready for the new shell when Catalina is released?
If you're thinking using the new default shell consider:
- If you want to give Zsh a whirl and feel some of the differences without changing shell settings on your machine you might try Powerline10k in a Docker container and see if it is your cup of tea.
- If you don't need all the bells and whistles and use Bash for just basic scripts it's rather easy to set your shell as others here have explained. And if you decide you want to use features in Bash 5 it's a fairly trivial upgrade for macOS.
- If you want to improve the portability of your scripts so they're more likely to function as expected in both shells, test them for POSIX-compliance and remove any "bashisms". I've used ShellCheck for this and it works quite well for less complicated scripts.
While no particular path is clear these three approaches should give you enough confidence to make an informed decision without over engineering the problem or solution space.
edited Oct 10 at 8:36
answered Oct 10 at 8:23
Josh HabdasJosh Habdas
1501 silver badge5 bronze badges
1501 silver badge5 bronze badges
add a comment
|
add a comment
|
16
All this hubbub that you have been reading is much to do about nothing. The OS assigns a "default" shell when creating new users, no reason more . Bash isn't going away, and you can use that as your shell or any of the other shells currently offered.
– fd0
Jun 8 at 12:27
1
Speaking as someone who's used both and landed on bash -- the only thing that made me really, deeply unhappy with zsh was its decision to break POSIX compliance when the standard canonicalizes admittedly-bad design decisions in ways that made it easy to get sloppy about correctness when trying to write scripts that needed to be compatible with other, strictly-POSIX-superset shells. Unfortunately, that "only thing" can be a pretty big one. Still, ksh93 isn't going away, and anyone serious about bash wouldn't use the ancient 3.x release Apple ships anyhow.
– Charles Duffy
Jun 10 at 16:29
As a follow-up - installed Catalina yesterday, switched to zsh, imported bash_history and copied over some of my preferred aliases from bash_profile, nothing seems to have broken. Appreciate all of the information provided by everyone here and hopefully it helps others out as well.
– dr.nixon
Oct 9 at 17:07