很多人把“环境”直接理解成 Bash、Zsh 或 /bin/sh。这样说不算错,但不够准确。Shell 只是环境的核心部分,环境还包括环境变量、当前目录、启动文件、别名、函数、历史记录和补全设置。
如果只是打开终端执行命令,不同 Shell 的差异可能不明显。写脚本时,解释器选错了,就可能出现本机运行正常,换一台机器却直接报错的情况。
常见的 Shell
Bash
Bash 是 Bourne Again Shell,兼容大量传统 Bourne Shell 语法,也提供数组、命令历史、补全和较丰富的脚本功能。Linux 服务器上很常见,资料和示例也多。
它适合日常交互和明确面向 Bash 的脚本。不过,Bash 并不是所有 Unix 系统的默认 Shell,不能因为它常见,就假定目标机器一定安装了 Bash。
sh
sh 更准确的理解是一个 Shell 兼容接口,通常用于执行 POSIX Shell 脚本。现代系统中的 /bin/sh 可能链接到 dash、Bash 的兼容模式,或者其他实现,它不一定就是历史上的 Bourne Shell。
可以用下面的命令查看当前系统实际使用的程序:
command -v sh
ls -l "$(command -v sh)"
如果脚本只使用 POSIX 标准语法,通常可以写成:
#!/bin/sh
但不要在这种脚本里使用 Bash 数组、[[ ... ]]、进程替换等 Bash 专有功能。
Zsh
Zsh 在交互使用方面很强,补全、历史搜索、主题和插件生态都比较丰富。macOS 把 Zsh 作为默认登录 Shell,因此很多 Mac 用户会直接接触到它。
Zsh 的路径不一定是 /bin/zsh,可以用下面的命令查找:
command -v zsh
Zsh 适合作为个人日常 Shell,但不建议把依赖 Zsh 特有语法的脚本伪装成通用 Shell 脚本。
Ksh
Ksh,即 KornShell,延续了 Bourne Shell 一系的语法,并加入了更丰富的交互和脚本能力。它常见于传统 Unix 和部分企业系统。
Ksh 与 Csh 不是同一套语法,也不能说 Ksh 兼容 Csh。选择 Ksh 时,需要确认目标系统安装的是哪一种实现,以及脚本使用了哪些扩展。
Csh 和 Tcsh
Csh 的部分控制结构受到 C 语言影响,Tcsh 是它的改进版本,增加了补全等交互功能。它们在一些传统 Unix 环境中仍然可以见到。
Csh 家族不适合作为新脚本的默认选择。脚本可移植性和错误处理更容易遇到问题,除非维护已有 Csh 脚本或目标系统明确要求,否则优先考虑 POSIX Shell、Bash 或其他更适合脚本的 Shell。
为什么会有这么多 Shell
不同 Shell 的出现,和 Unix 的历史演进有关。早期 Shell 解决的是“如何执行命令”,后来的实现逐渐增加了脚本能力、交互补全、历史搜索和定制功能。
它们之间的差异主要体现在三个地方:
- 交互功能不同,例如补全、历史搜索和主题定制。
- 脚本语法不同,例如数组、条件判断和字符串处理方式。
- 系统默认值不同,例如某个系统的
/bin/sh实际链接到哪一种实现。
所以“哪个 Shell 最好”通常没有统一答案。日常使用和脚本执行,是两个需要分开考虑的问题。
如何选择
日常在 Linux 或 macOS 上使用终端,可以按个人习惯选择 Bash 或 Zsh。喜欢强补全和插件,可以使用 Zsh;希望示例多、迁移经验广,Bash 通常更容易上手。
编写需要跨 Linux、BSD、macOS 等系统运行的脚本时,先判断是否真的需要某个 Shell 的扩展:
- 只使用 POSIX 语法:使用
#!/bin/sh。 - 使用 Bash 数组、
[[ ... ]]等功能:使用#!/usr/bin/env bash或目标系统明确存在的 Bash 路径。 - 维护旧的 Csh、Ksh 脚本:保持原解释器,并先确认目标系统的实现和版本。
解释器声明只是第一步。脚本还要在目标系统上检查命令是否存在、选项是否一致,以及路径和权限是否满足要求。Shell 名称相同,不代表所有外部命令的行为完全相同。
一句话总结
Shell 环境不只是一个命令行窗口。Bash、Zsh、Ksh、Csh 和 sh 的定位不同:交互使用可以按习惯选择,脚本则应该先确定兼容范围,再选择解释器和语法。