🇯🇵 日本語 | 🇺🇸 English | 🇪🇸 Español | 🇵🇹 Português | 🇹🇭 ไทย | 🇨🇳 中文

Bash vs Dash:编写Shell脚本该用哪个?比较与选择指南

在运营和开发网站时,我们总想把一些固定的小任务自动化,对吧?这时候大显身手的就是“Shell脚本”。但是,一旦开始编写,你是否曾对文件第一行像咒语一样的#!/bin/bash#!/bin/sh产生过“这到底有什么区别?”的疑问呢?

实际上,这个区别非常重要。特别是为了避免“在自己电脑上运行得好好的,一放到服务器上就报错!”这样的悲剧,这些知识是必不可少的。本文将为初学者通俗易懂地讲解两种常用Shell——BashDash的区别,并提供可以复制粘贴即可尝试的代码,进行彻底解说。读完本文,你也能写出不受环境影响的、健壮的Shell脚本!


Bash和Dash,它们到底是什么?

首先,我们来介绍一下两位主角(两种Shell)。了解它们各自擅长的事情和性格,是与它们“交朋友”的第一步。

Bash (Bourne-Again SHell) - 大家的好伙伴,功能丰富的Shell

在许多Linux系统和旧版macOS中,Bash被用作我们打开终端时首先启动的“登录Shell”。用方向键翻阅命令历史、用Tab键补全命令和文件名、使用彩色显示……我们平时不经意间使用的许多便利功能,大多都是由Bash提供的。可以说,它是一种非常适合人类直接操作(交互式使用)的、功能强大且友好的Shell。

Dash (Debian Almquist SHell) - 幕后英雄,高速轻量的Shell

另一方面,Dash的特点就是极致的轻量和高速。因为它削减了多余的功能,所以特别擅长快速执行脚本。正因为这个特点,在Ubuntu等基于Debian的操作系统中,它被用作系统启动脚本和指定#!/bin/sh时的默认Shell。也就是说,即使我们直接接触它的机会不多,但在系统底层,Dash正在努力地工作着。


最大的分水岭:作为“方言”与“普通话”的POSIX标准

Bash和Dash最大的区别在于它们对POSIX这个标准的遵守程度。

突然出现的专业术语可能会让你有些困惑。简单来说,POSIX就像是“Shell脚本界的普通话”。用这种普通话编写的脚本,可以保证在任何环境(操作系统)下都能以相同的方式运行。

我们平时在终端中使用的便利功能,很多时候都是Bash的“方言”。而在许多系统中,/bin/sh指向的是“只懂普通话的Shell”(如Dash)。这就是“在我的PC(Bash)上能运行,但在服务器(/bin/sh)上就运行不了”现象的主要原因。

在脚本的第一行写上#!/bin/bash,就相当于声明:“这个脚本要用Bash的方言哦!”,这样就可以使用Bash的便利功能了。相反,写#!/bin/sh则相当于声明:“请用普通话!”,如果想提高可移植性和兼容性,使用#!/bin/sh并有意识地采用POSIX兼容的写法,才是上策。


【实践】代码比较!Bash和Dash的写法有何不同?

从这里开始,我们通过具体的代码来比较一下Bash的“方言”和在Dash中也能运行的POSIX兼容“普通话”写法。为了体验“能运行”的感觉,请务必复制到你的终端中执行一下!

1. 数组的处理

当想一次性处理多个值时,我们会使用数组。在Bash中可以直观地编写,但POSIX标准中没有数组的规范。因此,要在Dash中做同样的事情,需要一些技巧。

Bash的写法(方言)

可以使用()轻松定义数组,并像${my_array[1]}这样通过指定索引来获取元素。

#!/bin/bash

# 定义数组
fruits=("apple" "banana" "cherry")

# 输出第二个元素(索引从0开始)
echo ${fruits[1]}

执行结果:

banana

在Dash中也能运行的写法(普通话)

在编写POSIX兼容代码时,通常使用空格分隔的字符串或Shell的位置参数($1, $2...)来代替数组。这里介绍使用for循环处理字符串的方法,这是一种常见的模式。

#!/bin/sh

# 定义为空格分隔的字符串
fruits="apple banana cherry"

# 使用for循环依次输出所有元素
for fruit in $fruits; do
  echo "I like $fruit!"
done

执行结果:

I like apple!
I like banana!
I like cherry!

2. 条件分支的写法

“如果XX,则YY”这样的条件分支是脚本的基础。这里也存在很大的差异。

Bash的写法(方言)

在Bash中,可以使用功能更强大的测试命令[[ ... ]]。它非常方便,可以用&&连接多个条件,用==进行字符串比较,甚至可以进行模式匹配。

#!/bin/bash

name="Taro"
age=25

# 用&&连接两个条件
if [[ "$name" == "Taro" && "$age" -gt 20 ]]; then
  echo "Welcome, Taro!"
fi

执行结果:

Welcome, Taro!

在Dash中也能运行的写法(普通话)

POSIX兼容的测试命令是[ ... ]。这是一个很早就有的命令,存在一些限制。例如,字符串比较使用单个=,多个条件需要使用-a (AND) 或-o (OR),或者组合使用多个[ ]。为了安全起见,推荐组合使用多个[ ]

#!/bin/sh

name="Taro"
age=25

# 使用两个 [ ] 来描述条件
if [ "$name" = "Taro" ] && [ "$age" -gt 20 ]; then
  echo "Welcome, Taro!"
fi

执行结果 (相同):

Welcome, Taro!

3. 字符串替换

希望将变量中的特定字符替换为其他字符,这种情况也很常见。

Bash的写法(方言)

在Bash中,可以使用${变量/替换前/替换后}这种写法轻松替换字符串。

#!/bin/bash

filename="photo_2025.jpg"

# 将"jpg"替换为"png"
new_filename=${filename/jpg/png}

echo $new_filename

执行结果:

photo_2025.png

在Dash中也能运行的写法(普通话)

在编写POSIX兼容代码时,通常会组合使用sedawk等外部命令来进行字符串操作。这里介绍一个使用sed的例子。虽然代码会稍长一些,但能确保在任何环境下都能稳定运行,让人放心。

#!/bin/sh

filename="photo_2025.jpg"

# 用echo将变量内容传递给sed命令,进行替换处理
new_filename=$(echo "$filename" | sed 's/jpg/png/')

echo "$new_filename"

执行结果 (相同):

photo_2025.png

结论:到底该用哪个?

看了这么多区别,大家最想知道的肯定是“那我到底该用哪个?”吧。答案是“视情况而定”,但对于初学者,我们有推荐的指导方针。

根据不同用例的推荐选择

给初学者的建议

如果你刚开始学习Shell脚本,我们建议你从一开始就使用`#!/bin/sh`,学习POSIX兼容的写法。为什么呢?因为“大的能兼容小的”。用POSIX兼容方式写的脚本在Bash中也能正常运行,但反之则不然。一开始就掌握“普通话”,那么无论到什么环境都不会遇到困难。


注意!那些容易不小心使用的Bash“方言”(Bashism)

本以为是好心写出的代码,结果却是Bash的“方言”……这种情况很常见。这里介绍几个特别容易犯的“Bashism(Bash主义)”。

只要避免这些Bashism,你的脚本的兼容性就会得到极大的提升。


总结:灵活运用Bash的便利与Dash的稳健!

这次,我们通过Bash和Dash的区别,解说了如何编写高兼容性的Shell脚本。

掌握了这种思维方式,就能防范“因环境问题无法运行”的麻烦,写出在更多场合都有用的脚本。来吧,从今天起,你也来挑战一下编写“在哪里都能运行的Shell脚本”吧!


接下来也请阅读这篇文章

Dash脚本示例集(if语句、循环、简单函数)