Zsh(Z Shell)与 Bash(Bourne Again Shell)是当今使用最广泛的两个 Unix Shell。虽然二者在语法层面高度相似,但在设计理念、默认行为、交互体验和配置体系上存在根本性差异。理解这些差异对于编写可移植脚本、配置高效开发环境至关重要。
一、核心哲学差异
#mermaid-svg-BIxom4kSQz2i696j{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BIxom4kSQz2i696j .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BIxom4kSQz2i696j .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BIxom4kSQz2i696j .error-icon{fill:#552222;}#mermaid-svg-BIxom4kSQz2i696j .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BIxom4kSQz2i696j .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BIxom4kSQz2i696j .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BIxom4kSQz2i696j .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BIxom4kSQz2i696j .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BIxom4kSQz2i696j .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BIxom4kSQz2i696j .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BIxom4kSQz2i696j .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BIxom4kSQz2i696j .marker.cross{stroke:#333333;}#mermaid-svg-BIxom4kSQz2i696j svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BIxom4kSQz2i696j p{margin:0;}#mermaid-svg-BIxom4kSQz2i696j .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-BIxom4kSQz2i696j .cluster-label text{fill:#333;}#mermaid-svg-BIxom4kSQz2i696j .cluster-label span{color:#333;}#mermaid-svg-BIxom4kSQz2i696j .cluster-label span p{background-color:transparent;}#mermaid-svg-BIxom4kSQz2i696j .label text,#mermaid-svg-BIxom4kSQz2i696j span{fill:#333;color:#333;}#mermaid-svg-BIxom4kSQz2i696j .node rect,#mermaid-svg-BIxom4kSQz2i696j .node circle,#mermaid-svg-BIxom4kSQz2i696j .node ellipse,#mermaid-svg-BIxom4kSQz2i696j .node polygon,#mermaid-svg-BIxom4kSQz2i696j .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-BIxom4kSQz2i696j .rough-node .label text,#mermaid-svg-BIxom4kSQz2i696j .node .label text,#mermaid-svg-BIxom4kSQz2i696j .image-shape .label,#mermaid-svg-BIxom4kSQz2i696j .icon-shape .label{text-anchor:middle;}#mermaid-svg-BIxom4kSQz2i696j .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-BIxom4kSQz2i696j .rough-node .label,#mermaid-svg-BIxom4kSQz2i696j .node .label,#mermaid-svg-BIxom4kSQz2i696j .image-shape .label,#mermaid-svg-BIxom4kSQz2i696j .icon-shape .label{text-align:center;}#mermaid-svg-BIxom4kSQz2i696j .node.clickable{cursor:pointer;}#mermaid-svg-BIxom4kSQz2i696j .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-BIxom4kSQz2i696j .arrowheadPath{fill:#333333;}#mermaid-svg-BIxom4kSQz2i696j .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-BIxom4kSQz2i696j .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-BIxom4kSQz2i696j .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BIxom4kSQz2i696j .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-BIxom4kSQz2i696j .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BIxom4kSQz2i696j .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-BIxom4kSQz2i696j .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-BIxom4kSQz2i696j .cluster text{fill:#333;}#mermaid-svg-BIxom4kSQz2i696j .cluster span{color:#333;}#mermaid-svg-BIxom4kSQz2i696j div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-BIxom4kSQz2i696j .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-BIxom4kSQz2i696j rect.text{fill:none;stroke-width:0;}#mermaid-svg-BIxom4kSQz2i696j .icon-shape,#mermaid-svg-BIxom4kSQz2i696j .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BIxom4kSQz2i696j .icon-shape p,#mermaid-svg-BIxom4kSQz2i696j .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-BIxom4kSQz2i696j .icon-shape .label rect,#mermaid-svg-BIxom4kSQz2i696j .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BIxom4kSQz2i696j .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-BIxom4kSQz2i696j .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-BIxom4kSQz2i696j :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Zsh 设计哲学
交互体验优先
功能丰富性
可定制性
开发者生产力
Bash 设计哲学
POSIX 合规优先
跨平台可移植性
脚本执行效率
最小资源占用
根本分歧:Bash 以"在任何地方都能运行"为目标,Zsh 以"让终端工作更愉悦"为目标。这一哲学差异决定了二者在几乎所有技术细节上的走向。
二、关键行为差异一览
| 对比维度 | Bash | Zsh |
|---|---|---|
| 数组索引起始 | 0-based | 1-based(可 KSH_ARRAYS 切换) |
| 默认词法切分 | 执行(按 IFS 拆分) | 不执行(需 $=var 或 SH_WORD_SPLIT) |
| 默认路径扩展 | 执行 Globbing | 不执行(需 $~var) |
| 扩展 Globbing | 需 shopt -s extglob |
默认启用 EXTENDED_GLOB |
| 浮点数运算 | 不支持(整数 only) | 原生支持 |
| 右侧提示符 | 不支持 | 原生支持 RPROMPT |
| 拼写纠正 | 不支持 | 内置 setopt CORRECT |
| 共享历史 | 需手动配置 | 内置 setopt SHARE_HISTORY |
| 选项设置 | set -o / shopt -s |
setopt / unsetopt |
| 启动时间(裸) | ~38 ms | ~82 ms |
| 内存占用(裸) | ~1.5 MB | ~3.2 MB |
| POSIX 合规性 | 完全合规 | 部分合规(emulate sh 模式) |
| macOS 默认 | 否(Catalina 后弃用) | 是(Catalina 起) |
| Linux 默认 | 是 | 否 |
三、词法切分:最经典的差异
这是从 Bash 迁移到 Zsh 时最常见的"坑",也是 Zsh FAQ 中排名第一的问题。
3.1 Bash 的默认行为
bash
# Bash
args="-a -l"
ls $args
# 实际执行:ls -a -l(词法切分后变成两个参数)
Bash 对未加引号的变量扩展默认执行词法切分(按 IFS 拆分)和路径扩展(Globbing)。
3.2 Zsh 的默认行为
zsh
# Zsh
args="-a -l"
ls $args
# 实际执行:ls "-a -l"(作为一个整体参数)
# 报错:ls: invalid option -- ' '
Zsh 默认不对未加引号的变量扩展执行词法切分和路径扩展。这在 Zsh 设计者看来才是正确的行为------Bash 的隐式拆分被 POSIX 标准强制要求,但本质上是一个设计缺陷。
3.3 兼容性处理
zsh
# 方法1:显式启用 Bash 风格词法切分
setopt SH_WORD_SPLIT
# 方法2:单次使用 $= 前缀
ls ${=args}
# 方法3:使用数组(推荐)
args=(-a -l)
ls "${args[@]}"
四、数组索引:0 vs 1
4.1 根本差异
bash
# Bash
arr=(a b c)
echo "${arr[0]}" # 输出:a(第一个元素)
echo "${arr[1]}" # 输出:b
zsh
# Zsh
arr=(a b c)
echo "$arr[1]" # 输出:a(第一个元素)
echo "$arr[2]" # 输出:b
echo "$arr[0]" # 输出:a b c(整个数组!)
Zsh 的数组索引起始为 1,与绝大多数 Shell(csh、tcsh、fish、yash)一致。Bash 的 0-based 索引实际上是复制了 ksh 的设计。
4.2 Zsh 的设计理由
- 与
$@(位置参数从$1开始)保持一致 - 与
$argv(csh 风格)保持一致 - 数组长度 = 最后一个索引(非稀疏数组)
- 负索引
$arr[-1]表示最后一个元素(0-based 无法实现-0)
4.3 兼容性处理
zsh
# 启用 ksh/bash 风格数组
setopt KSH_ARRAYS
# 或始终使用偏移语法(跨 Shell 兼容)
echo "${arr[*]:0:1}" # 第一个元素(offset 始终 0-based)
五、Globbing 与路径扩展
5.1 Bash 的受限 Globbing
bash
# Bash 基础通配符
ls *.txt # 匹配所有 .txt 文件
ls file?.txt # 匹配 file1.txt, fileA.txt 等
# 扩展 Globbing(需显式启用)
shopt -s extglob
ls !(README).txt # 排除 README.txt
ls @(foo|bar).txt # 匹配 foo.txt 或 bar.txt
5.2 Zsh 的强大 Globbing
zsh
# Zsh 默认启用 EXTENDED_GLOB
setopt EXTENDED_GLOB # 通常已默认启用
# 否定匹配
ls ^README.txt # 排除 README.txt
# 括号或
ls (foo|bar).txt
# Glob 限定符(Zsh 独有)
ls *.txt(N) # N: 无匹配时返回空列表(不报错)
ls *.txt(om) # om: 按修改时间排序(最旧在前)
ls *.txt(Om) # Om: 按修改时间排序(最新在前)
ls *.txt(Lk+100) # 匹配大于 100KB 的文件
ls *.txt(^D) # 排除目录
# 递归 Globbing
ls **/*.txt # 递归匹配所有子目录的 .txt
5.3 关键陷阱
bash
# Bash 中无匹配时返回字面字符串
echo *.nonexistent # 输出:*.nonexistent
# Zsh 中无匹配时默认报错
echo *.nonexistent # zsh: no matches found: *.nonexistent
# Zsh 解决方案
setopt NO_NOMATCH # 无匹配时返回字面字符串
# 或
echo *.nonexistent(N) # 使用 N 限定符
六、算术运算
6.1 Bash:整数唯一
bash
# Bash 仅支持整数运算
echo $((3 + 4)) # 7
echo $((3 / 2)) # 1(截断小数)
echo $((3.5 + 2)) # 语法错误!
# 浮点数需借助外部工具
echo "scale=2; 3.5 + 2" | bc # 5.5
6.2 Zsh:原生浮点支持
zsh
# Zsh 原生支持浮点数
echo $((3.5 + 2)) # 5.5
echo $((3 / 2.0)) # 1.5
# 声明浮点变量
typeset -F 2 num # 2位小数精度
num=3.14159
echo $num # 3.14
# 加载数学函数模块
zmodload zsh/mathfunc
echo $((sin(3.14))) # 0.00159...
七、提示符系统
7.1 Bash 提示符
bash
# Bash 使用 PS1
PS1='\u@\h:\w\$ ' # user@host:dir$
# 颜色需手动添加 ANSI 转义
PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
# 无右侧提示符支持
7.2 Zsh 提示符
zsh
# Zsh 使用 PROMPT(或 PS1)
PROMPT='%n@%m:%~%# ' # user@host:dir%
# 丰富的转义序列
# %n = 用户名, %m = 主机名, %~ = 当前目录(~展开)
# %F{color} = 前景色, %K{color} = 背景色
# %B = 粗体, %U = 下划线
# 右侧提示符(Zsh 独有)
RPROMPT='%F{cyan}%D{%H:%M:%S}%f' # 右侧显示时间
# 拼写纠正提示符
setopt CORRECT
SPROMPT='zsh: correct '%R' to '%r' [nyae]? '
八、初始化文件体系
8.1 Bash 初始化文件
#mermaid-svg-DAMDqjZ8uktm3SJc{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-DAMDqjZ8uktm3SJc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-DAMDqjZ8uktm3SJc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-DAMDqjZ8uktm3SJc .error-icon{fill:#552222;}#mermaid-svg-DAMDqjZ8uktm3SJc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DAMDqjZ8uktm3SJc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-DAMDqjZ8uktm3SJc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DAMDqjZ8uktm3SJc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DAMDqjZ8uktm3SJc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-DAMDqjZ8uktm3SJc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DAMDqjZ8uktm3SJc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DAMDqjZ8uktm3SJc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DAMDqjZ8uktm3SJc .marker.cross{stroke:#333333;}#mermaid-svg-DAMDqjZ8uktm3SJc svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DAMDqjZ8uktm3SJc p{margin:0;}#mermaid-svg-DAMDqjZ8uktm3SJc .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-DAMDqjZ8uktm3SJc .cluster-label text{fill:#333;}#mermaid-svg-DAMDqjZ8uktm3SJc .cluster-label span{color:#333;}#mermaid-svg-DAMDqjZ8uktm3SJc .cluster-label span p{background-color:transparent;}#mermaid-svg-DAMDqjZ8uktm3SJc .label text,#mermaid-svg-DAMDqjZ8uktm3SJc span{fill:#333;color:#333;}#mermaid-svg-DAMDqjZ8uktm3SJc .node rect,#mermaid-svg-DAMDqjZ8uktm3SJc .node circle,#mermaid-svg-DAMDqjZ8uktm3SJc .node ellipse,#mermaid-svg-DAMDqjZ8uktm3SJc .node polygon,#mermaid-svg-DAMDqjZ8uktm3SJc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-DAMDqjZ8uktm3SJc .rough-node .label text,#mermaid-svg-DAMDqjZ8uktm3SJc .node .label text,#mermaid-svg-DAMDqjZ8uktm3SJc .image-shape .label,#mermaid-svg-DAMDqjZ8uktm3SJc .icon-shape .label{text-anchor:middle;}#mermaid-svg-DAMDqjZ8uktm3SJc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-DAMDqjZ8uktm3SJc .rough-node .label,#mermaid-svg-DAMDqjZ8uktm3SJc .node .label,#mermaid-svg-DAMDqjZ8uktm3SJc .image-shape .label,#mermaid-svg-DAMDqjZ8uktm3SJc .icon-shape .label{text-align:center;}#mermaid-svg-DAMDqjZ8uktm3SJc .node.clickable{cursor:pointer;}#mermaid-svg-DAMDqjZ8uktm3SJc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-DAMDqjZ8uktm3SJc .arrowheadPath{fill:#333333;}#mermaid-svg-DAMDqjZ8uktm3SJc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-DAMDqjZ8uktm3SJc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-DAMDqjZ8uktm3SJc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DAMDqjZ8uktm3SJc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-DAMDqjZ8uktm3SJc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DAMDqjZ8uktm3SJc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-DAMDqjZ8uktm3SJc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-DAMDqjZ8uktm3SJc .cluster text{fill:#333;}#mermaid-svg-DAMDqjZ8uktm3SJc .cluster span{color:#333;}#mermaid-svg-DAMDqjZ8uktm3SJc div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-DAMDqjZ8uktm3SJc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-DAMDqjZ8uktm3SJc rect.text{fill:none;stroke-width:0;}#mermaid-svg-DAMDqjZ8uktm3SJc .icon-shape,#mermaid-svg-DAMDqjZ8uktm3SJc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DAMDqjZ8uktm3SJc .icon-shape p,#mermaid-svg-DAMDqjZ8uktm3SJc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-DAMDqjZ8uktm3SJc .icon-shape .label rect,#mermaid-svg-DAMDqjZ8uktm3SJc .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DAMDqjZ8uktm3SJc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-DAMDqjZ8uktm3SJc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-DAMDqjZ8uktm3SJc :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
否
Shell 启动
Login Shell?
/etc/profile
Interactive?
~/.bash_profile
或 ~/.bash_login
或 ~/.profile
source ~/.bashrc
~/.bashrc
BASH_ENV 变量
/etc/bash.bashrc
| 文件 | 执行时机 | 用途 |
|---|---|---|
/etc/profile |
登录 Shell | 系统级环境变量 |
~/.bash_profile |
登录 Shell | 用户级环境变量、PATH |
~/.bashrc |
交互式非登录 Shell | 别名、函数、提示符 |
~/.bash_logout |
登录 Shell 退出 | 清理操作 |
8.2 Zsh 初始化文件
#mermaid-svg-ZAx0DQ87RC2WxwJz{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ZAx0DQ87RC2WxwJz .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZAx0DQ87RC2WxwJz .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZAx0DQ87RC2WxwJz .error-icon{fill:#552222;}#mermaid-svg-ZAx0DQ87RC2WxwJz .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZAx0DQ87RC2WxwJz .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZAx0DQ87RC2WxwJz .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZAx0DQ87RC2WxwJz .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZAx0DQ87RC2WxwJz .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZAx0DQ87RC2WxwJz .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZAx0DQ87RC2WxwJz .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZAx0DQ87RC2WxwJz .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZAx0DQ87RC2WxwJz .marker.cross{stroke:#333333;}#mermaid-svg-ZAx0DQ87RC2WxwJz svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZAx0DQ87RC2WxwJz p{margin:0;}#mermaid-svg-ZAx0DQ87RC2WxwJz .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ZAx0DQ87RC2WxwJz .cluster-label text{fill:#333;}#mermaid-svg-ZAx0DQ87RC2WxwJz .cluster-label span{color:#333;}#mermaid-svg-ZAx0DQ87RC2WxwJz .cluster-label span p{background-color:transparent;}#mermaid-svg-ZAx0DQ87RC2WxwJz .label text,#mermaid-svg-ZAx0DQ87RC2WxwJz span{fill:#333;color:#333;}#mermaid-svg-ZAx0DQ87RC2WxwJz .node rect,#mermaid-svg-ZAx0DQ87RC2WxwJz .node circle,#mermaid-svg-ZAx0DQ87RC2WxwJz .node ellipse,#mermaid-svg-ZAx0DQ87RC2WxwJz .node polygon,#mermaid-svg-ZAx0DQ87RC2WxwJz .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ZAx0DQ87RC2WxwJz .rough-node .label text,#mermaid-svg-ZAx0DQ87RC2WxwJz .node .label text,#mermaid-svg-ZAx0DQ87RC2WxwJz .image-shape .label,#mermaid-svg-ZAx0DQ87RC2WxwJz .icon-shape .label{text-anchor:middle;}#mermaid-svg-ZAx0DQ87RC2WxwJz .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ZAx0DQ87RC2WxwJz .rough-node .label,#mermaid-svg-ZAx0DQ87RC2WxwJz .node .label,#mermaid-svg-ZAx0DQ87RC2WxwJz .image-shape .label,#mermaid-svg-ZAx0DQ87RC2WxwJz .icon-shape .label{text-align:center;}#mermaid-svg-ZAx0DQ87RC2WxwJz .node.clickable{cursor:pointer;}#mermaid-svg-ZAx0DQ87RC2WxwJz .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ZAx0DQ87RC2WxwJz .arrowheadPath{fill:#333333;}#mermaid-svg-ZAx0DQ87RC2WxwJz .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ZAx0DQ87RC2WxwJz .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ZAx0DQ87RC2WxwJz .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZAx0DQ87RC2WxwJz .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ZAx0DQ87RC2WxwJz .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZAx0DQ87RC2WxwJz .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ZAx0DQ87RC2WxwJz .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ZAx0DQ87RC2WxwJz .cluster text{fill:#333;}#mermaid-svg-ZAx0DQ87RC2WxwJz .cluster span{color:#333;}#mermaid-svg-ZAx0DQ87RC2WxwJz div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ZAx0DQ87RC2WxwJz .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ZAx0DQ87RC2WxwJz rect.text{fill:none;stroke-width:0;}#mermaid-svg-ZAx0DQ87RC2WxwJz .icon-shape,#mermaid-svg-ZAx0DQ87RC2WxwJz .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZAx0DQ87RC2WxwJz .icon-shape p,#mermaid-svg-ZAx0DQ87RC2WxwJz .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ZAx0DQ87RC2WxwJz .icon-shape .label rect,#mermaid-svg-ZAx0DQ87RC2WxwJz .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZAx0DQ87RC2WxwJz .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ZAx0DQ87RC2WxwJz .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ZAx0DQ87RC2WxwJz :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
是
Shell 启动
/etc/zshenv
~/.zshenv
始终加载
Login Shell?
/etc/zprofile
~/.zprofile
Interactive?
/etc/zshrc
~/.zshrc
Login Shell?
/etc/zlogin
~/.zlogin
退出时
~/.zlogout
| 文件 | 执行时机 | 用途 |
|---|---|---|
~/.zshenv |
始终 | 环境变量(PATH 等) |
~/.zprofile |
登录 Shell | 环境设置(Bash 的 .bash_profile 对应) |
~/.zshrc |
交互式 Shell | 别名、函数、提示符、选项 |
~/.zlogin |
登录 Shell(.zshrc 之后) | 登录后执行的外部命令 |
~/.zlogout |
登录 Shell 退出 | 清理操作 |
关键区别 :Zsh 的 ~/.zshenv 在所有 Shell 调用时都会加载(包括非交互式脚本),而 Bash 没有对应文件。这使得 Zsh 的环境变量配置更集中。
九、选项系统
9.1 Bash 的双层选项
bash
# POSIX 选项(set -o)
set -o errexit # 等价于 set -e
set -o nounset # 等价于 set -u
set -o pipefail
# Bash 特有选项(shopt)
shopt -s extglob # 启用扩展 Globbing
shopt -s nullglob # 无匹配时返回空
shopt -s dotglob # 包含隐藏文件
shopt -s globstar # 启用 ** 递归匹配
9.2 Zsh 的统一选项
zsh
# 所有选项通过 setopt/unsetopt 管理
setopt EXTENDED_GLOB # 扩展 Globbing(默认启用)
setopt NO_NOMATCH # 无匹配时不报错
setopt CORRECT # 拼写纠正
setopt SHARE_HISTORY # 跨会话共享历史
setopt AUTO_CD # 输入目录名自动 cd
setopt AUTO_PUSHD # cd 自动 pushd
setopt CHASE_LINKS # 解析符号链接
十、交互功能对比
10.1 Tab 补全
| 特性 | Bash | Zsh |
|---|---|---|
| 基础补全 | 支持 | 支持 |
| 菜单选择 | 需配置 | 默认支持 |
| 描述信息 | 不支持 | 支持(命令+描述) |
| 模糊匹配 | 不支持 | 支持 |
| 大小写不敏感 | 需配置 | 支持 |
| 补全 Git 分支 | 需插件 | 内置/插件丰富 |
zsh
# Zsh 补全示例
$ git che<TAB>
check-attr -- display gitattributes information
check-ref-format -- ensure that a reference name is well formed
checkout -- checkout branch or paths to working tree
checkout-index -- copy files from index to working directory
cherry -- find commits not merged upstream
cherry-pick -- apply changes introduced by some existing commits
10.2 历史管理
zsh
# Zsh 共享历史(跨所有终端会话)
setopt SHARE_HISTORY
# 历史相关选项
setopt HIST_IGNORE_DUPS # 忽略重复命令
setopt HIST_IGNORE_SPACE # 忽略以空格开头的命令
setopt HIST_REDUCE_BLANKS # 去除多余空格
setopt APPEND_HISTORY # 追加而非覆盖
Bash 默认每个终端会话维护自己的历史缓冲区,只有会话结束时才写入 .bash_history。
十一、脚本可移植性
11.1 最佳实践:Zsh 交互 + Bash 脚本
bash
#!/usr/bin/env bash
# 所有脚本使用 Bash shebang,确保可移植性
set -euo pipefail
# ...
zsh
# ~/.zshrc
# 交互环境使用 Zsh,享受丰富功能
setopt CORRECT
setopt SHARE_HISTORY
# ...
11.2 Zsh 的 emulate 模式
zsh
# 在 Zsh 中临时模拟 sh/bash
emulate sh -c 'source my_bash_script.sh'
# 在函数内局部模拟
my_func() {
emulate -L bash # -L 表示局部选项
# 函数体使用 bash 语义
}
# 检查当前模拟模式
echo $(emulate) # 输出:zsh 或 sh 或 ksh
十二、性能对比
| 指标 | Bash | Zsh(裸) | Zsh + Oh My Zsh |
|---|---|---|---|
| 启动时间 | ~38 ms | ~82 ms | 500--1000+ ms |
| 内存占用 | ~1.5 MB | ~3.2 MB | 15--25 MB |
| 1000万次整数循环 | 14.2 s | 15.8 s | --- |
| 10000次子 Shell 创建 | 4.1 s | 7.3 s | --- |
结论:Bash 在启动时间和资源占用上优势明显,适合脚本和容器环境;Zsh 的交互功能带来的生产力提升远超其性能开销。
十三、迁移指南
13.1 Bash → Zsh
zsh
# 1. 复制基础配置
cp ~/.bashrc ~/.zshrc # 然后手动调整
# 2. 处理词法切分差异
# 在 .zshrc 顶部添加(如需兼容旧习惯)
setopt SH_WORD_SPLIT
# 3. 处理数组索引
# 或使用 KSH_ARRAYS,或改写为偏移语法
# 4. 安装 Oh My Zsh(可选)
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
13.2 Zsh → Bash
bash
# 1. 复制配置
cp ~/.zshrc ~/.bashrc # 然后手动调整
# 2. 移除 Zsh 特有语法
# - $arr[1] → ${arr[0]}
# - ${=var} → $var
# - ${~var} → $var
# - **/*.txt → 启用 shopt -s globstar
# 3. 安装 bash-completion
sudo apt install bash-completion
十四、总结决策树
#mermaid-svg-OrNNUcgcyk9wAO4H{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-OrNNUcgcyk9wAO4H .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-OrNNUcgcyk9wAO4H .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-OrNNUcgcyk9wAO4H .error-icon{fill:#552222;}#mermaid-svg-OrNNUcgcyk9wAO4H .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-OrNNUcgcyk9wAO4H .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-OrNNUcgcyk9wAO4H .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-OrNNUcgcyk9wAO4H .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-OrNNUcgcyk9wAO4H .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-OrNNUcgcyk9wAO4H .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-OrNNUcgcyk9wAO4H .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-OrNNUcgcyk9wAO4H .marker{fill:#333333;stroke:#333333;}#mermaid-svg-OrNNUcgcyk9wAO4H .marker.cross{stroke:#333333;}#mermaid-svg-OrNNUcgcyk9wAO4H svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-OrNNUcgcyk9wAO4H p{margin:0;}#mermaid-svg-OrNNUcgcyk9wAO4H .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-OrNNUcgcyk9wAO4H .cluster-label text{fill:#333;}#mermaid-svg-OrNNUcgcyk9wAO4H .cluster-label span{color:#333;}#mermaid-svg-OrNNUcgcyk9wAO4H .cluster-label span p{background-color:transparent;}#mermaid-svg-OrNNUcgcyk9wAO4H .label text,#mermaid-svg-OrNNUcgcyk9wAO4H span{fill:#333;color:#333;}#mermaid-svg-OrNNUcgcyk9wAO4H .node rect,#mermaid-svg-OrNNUcgcyk9wAO4H .node circle,#mermaid-svg-OrNNUcgcyk9wAO4H .node ellipse,#mermaid-svg-OrNNUcgcyk9wAO4H .node polygon,#mermaid-svg-OrNNUcgcyk9wAO4H .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-OrNNUcgcyk9wAO4H .rough-node .label text,#mermaid-svg-OrNNUcgcyk9wAO4H .node .label text,#mermaid-svg-OrNNUcgcyk9wAO4H .image-shape .label,#mermaid-svg-OrNNUcgcyk9wAO4H .icon-shape .label{text-anchor:middle;}#mermaid-svg-OrNNUcgcyk9wAO4H .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-OrNNUcgcyk9wAO4H .rough-node .label,#mermaid-svg-OrNNUcgcyk9wAO4H .node .label,#mermaid-svg-OrNNUcgcyk9wAO4H .image-shape .label,#mermaid-svg-OrNNUcgcyk9wAO4H .icon-shape .label{text-align:center;}#mermaid-svg-OrNNUcgcyk9wAO4H .node.clickable{cursor:pointer;}#mermaid-svg-OrNNUcgcyk9wAO4H .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-OrNNUcgcyk9wAO4H .arrowheadPath{fill:#333333;}#mermaid-svg-OrNNUcgcyk9wAO4H .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-OrNNUcgcyk9wAO4H .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-OrNNUcgcyk9wAO4H .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OrNNUcgcyk9wAO4H .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-OrNNUcgcyk9wAO4H .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OrNNUcgcyk9wAO4H .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-OrNNUcgcyk9wAO4H .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-OrNNUcgcyk9wAO4H .cluster text{fill:#333;}#mermaid-svg-OrNNUcgcyk9wAO4H .cluster span{color:#333;}#mermaid-svg-OrNNUcgcyk9wAO4H div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-OrNNUcgcyk9wAO4H .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-OrNNUcgcyk9wAO4H rect.text{fill:none;stroke-width:0;}#mermaid-svg-OrNNUcgcyk9wAO4H .icon-shape,#mermaid-svg-OrNNUcgcyk9wAO4H .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OrNNUcgcyk9wAO4H .icon-shape p,#mermaid-svg-OrNNUcgcyk9wAO4H .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-OrNNUcgcyk9wAO4H .icon-shape .label rect,#mermaid-svg-OrNNUcgcyk9wAO4H .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OrNNUcgcyk9wAO4H .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-OrNNUcgcyk9wAO4H .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-OrNNUcgcyk9wAO4H :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 编写脚本/CI/CD
日常交互/开发
是
否
选择 Shell
主要用途?
选择 Bash
追求极简?
选择 Bash
或 dash/ash
选择 Zsh
#!/bin/bash
set -euo pipefail
Oh My Zsh/Zinit
- Powerlevel10k
最佳实践是双轨制:用 Zsh 作为交互 Shell 提升日常效率,用 Bash 编写所有需要分发的脚本确保可移植性。二者并非对立,而是互补。