Shell脚本精读 · S13-01 | POSIX sh 与 Bash:语法对照表

模块 :S13 可移植性与规范

篇号 :S13-01 / 42

预计阅读 :50 分钟

主线 :Bash(对照 POSIX sh)


文章目录

    • 本篇目标
    • [30 秒速览](#30 秒速览)
    • 正文
      • [1. 先看清 shebang](#1. 先看清 shebang)
      • [2. POSIX sh 与 Bash 关系(够用理解)](#2. POSIX sh 与 Bash 关系(够用理解))
      • [3. 总对照表(语法)](#3. 总对照表(语法))
      • [4. 条件表达式(最常踩坑)](#4. 条件表达式(最常踩坑))
        • [4.1 `[` vs `[[`](#4.1 [ vs [[)
        • [4.2 整数比较](#4.2 整数比较)
        • [4.3 组合条件](#4.3 组合条件)
      • [5. 参数展开](#5. 参数展开)
      • [6. 循环与控制](#6. 循环与控制)
      • [7. 函数与作用域](#7. 函数与作用域)
      • [8. `set` 选项](#8. set 选项)
      • [9. 读脚本:红旗列表](#9. 读脚本:红旗列表)
      • [10. 何时选 sh、何时选 Bash](#10. 何时选 sh、何时选 Bash)
      • [11. 从 Bash 迁到 sh 的替换思路(预览)](#11. 从 Bash 迁到 sh 的替换思路(预览))
      • [12. 与 ShellCheck](#12. 与 ShellCheck)
    • 读脚本检查清单
    • 练习
    • 下一篇预告

本篇目标

建立 POSIX sh 与 Bash 的语法对照 :读到 #!/bin/sh 却满篇 [[、(( )) 时能判断风险;写脚本时能决定坚持 POSIX 还是明确 Bash 。本篇是对照表 ;编写清单与 command/printf 在 S13-02。


30 秒速览

  • POSIX sh :各系统上的 /bin/sh (可能是 dash 、bash 的 posix 模式 等),语法最小公共子集。
  • Bash :扩展多------[[ 、(( )) 、数组、{1..10} 、pipefail、进程替换等。
  • shebang 与内容要一致 :#!/bin/sh 里不要写 Bash 专用语法。
  • 可移植 优先:[ 、test 、$(( )) 、printf 、. 、getopts。
  • 本专栏主线是 Bash ;需要 sh 时查本篇 + S13-02。

正文

1. 先看清 shebang

第一行 含义
#!/bin/sh 用系统 sh 解释(不保证是 Bash)
#!/usr/bin/env bash 明确要 Bash(专栏推荐)
#!/bin/bash Bash,路径写死(部分系统无此路径)

读脚本第一问:第一行承诺的运行时,是否支持下文里的 Bash 语法?

bash 复制代码
#!/bin/sh
if [[ -f "$f" ]]; then    # 错:[[ 不是 POSIX

常见情况:作者本机 /bin/sh 指向 bash ,脚本在 dash / Alpine busybox sh 上直接报错。


2. POSIX sh 与 Bash 关系(够用理解)

  • POSIX:标准文档规定 shell 语法子集。
  • 实现 :Linux 上 sh 常为 dash (Debian/Ubuntu)或 bash --posix;macOS 曾长期不同。
  • Bash :超集;bash script.sh 可跑 Bash 语法;sh script.sh 只能跑 POSIX(加实现少量扩展)。

专栏策略 :教学用 Bash;交付若要求 sh,按本篇改写法。


3. 总对照表(语法)

特性 POSIX sh Bash 专栏
条件 if [ ] ✅ ✅ S05-01
[[ ]] ❌ ✅ S05-03
正则 =~ ❌ ✅ [[ ]] S05-03
模式 == *.log ❌ ✅ [[ ]] S05-03
算术测试 (( )) ❌ ✅ S05-04
[ "$n" -eq 10 ] ✅ ✅ S05-01
let 部分 ✅ S05-04
function f() ❌(f() 可以) ✅ S08-01
local ❌ ✅ S08-02
数组 arr=( ) ❌ ✅ S03-04
${arr[@]} ❌ ✅ S03-04
${var:0:5} 子串 ❌ ✅ S03-02
${var:-def} 部分(POSIX 有子集) ✅ S03-02
for i in {1..10} ❌ ✅ S07-01
for (( )) ❌ ✅ S07-01
select ❌ ✅ S08-04
getopts ✅ ✅ S08-04
source ❌(用 .) ✅ 同 . S08-03
$(cmd) ✅ ✅ S09-03
进程替换 <( ) ❌ ✅ S09-04
set -o pipefail ❌ ✅ S01-04、S09-02
PIPESTATUS ❌ ✅ S09-02
&> 重定向 ❌ ✅ S09-01
here-string <<< ❌ ✅ S09-01
[[ 内 && --- ✅ S05-03
echo -e 不可移植 常用 用 printf S13-02
read -r ✅ ✅ S07-02
trap EXIT 部分 ✅ S11-01
flock 非 POSIX 命令 常见 S11-02
timeout 非 POSIX 命令 GNU S11-02

4. 条件表达式(最常踩坑)

4.1 [ vs [[
bash 复制代码
# POSIX
if [ "$a" = "$b" ]; then
if [ -f "$file" ]; then

# Bash only
if [[ "$a" == "$b" ]]; then
if [[ "$f" == *.log ]]; then
4.2 整数比较
bash 复制代码
# POSIX
[ "$n" -eq 10 ]

# Bash(脚本内常用,但 sh 不行)
(( n >= 10 ))
4.3 组合条件
bash 复制代码
# POSIX:两个 [ 或 -a(不推荐 -a)
[ "$a" = "x" ] && [ "$b" = "y" ]

# Bash
[[ "$a" == "x" && "$b" == "y" ]]

5. 参数展开

写法 POSIX sh Bash
$1 "$@" ✅ ✅
${var:-def} ✅ ✅
${var:=def} ✅ ✅
${#var} ✅ ✅
${var#prefix} ❌ ✅
${var/pat/rep} ❌ ✅
数组 "${a[@]}" ❌ ✅

写 #!/bin/sh 时避免 S03-02 里 Bash 专有展开;S13-02 给替代写法。


6. 循环与控制

bash 复制代码
# POSIX
for f in "$@"; do
while [ "$n" -lt 10 ]; do
  n=$(( n + 1 ))
done

# Bash only
for i in {1..10}; do
for (( i=0; i<10; i++ )); do

break / continue:POSIX 均有。


7. 函数与作用域

bash 复制代码
# POSIX 函数
myfunc() {
  echo "$1"
}

# Bash 扩展
myfunc() {
  local x=1
  declare -i n=0
}

export -f:Bash,非 POSIX。


8. set 选项

选项 POSIX sh Bash
set -e ✅ ✅
set -u 部分 ✅
set -o pipefail ❌ ✅
set -x ✅ ✅

#!/bin/sh + set -euo pipefail :在 dash 上 -u 通常可用 ,pipefail 不可用 (ShellCheck SC3043)。


9. 读脚本:红旗列表

见到 #!/bin/sh (或 dash)却出现:

  • [[ (( )) {1..10}
  • source (应用 .)
  • 数组、${var//x/y}
  • <( ) &>
  • select

→ 要么 shebang 错了,要么脚本在部分系统会挂 。应改为 #!/usr/bin/env bash 或改写成 POSIX(S13-02)。

见到 #!/usr/bin/env bash:可合理使用专栏全部 Bash 特性。


10. 何时选 sh、何时选 Bash

选 POSIX sh 选 Bash
嵌入式/极简 rootfs 开发机、CI、团队统一 Bash
明确要求 dash 兼容 需要 数组、[[、pipefail
被 sh 调用的极小片段 复杂逻辑、运维工具(专栏默认)

不要假设「sh 就是 bash」。


11. 从 Bash 迁到 sh 的替换思路(预览)

Bash POSIX 倾向
[[ -f f ]] [ -f f ]
(( $# < 1 )) [ "$#" -lt 1 ]
[[ $x == *.log ]] case $x in *.log)
${s#pre} sed/expr(笨但可移植)
source f . f
pipefail 拆开管道或显式查 $?

细节与 command/printf 在 S13-02。


12. 与 ShellCheck

bash 复制代码
shellcheck -s sh script.sh    # 按 POSIX 扫
shellcheck -s bash script.sh  # 按 Bash 扫

shebang 与 -s 一致。见 S12-02。


读脚本检查清单

  • 第一行 shebang 与文内语法是否一致?
  • #!/bin/sh 下是否误用 [[、数组、pipefail?
  • 需要 模式匹配 时,sh 是否改用 case?
  • 整数比较是否用 -eq 而非 (( ))(在 sh 脚本里)?
  • 是否用 printf 代替 echo -e(S13-02)?

练习

判断题

  1. #!/bin/sh 保证脚本在所有 Unix 上由 Bash 执行。
  2. [[ 是 Bash 扩展,不是 POSIX。
  3. getopts 可在 POSIX sh 脚本中使用。
  4. set -o pipefail 在所有 /bin/sh 实现上都可用。
  5. 读脚本时 shebang 与语法冲突应优先信语法还是信 shebang?------应让它们一致。

参考答案

  1. 错。
  2. 对。
  3. 对。
  4. 错。
  5. 应一致;冲突时按更严格的 sh 理解或改 shebang。

改错题

下面脚本声称兼容 sh,请指出 Bash 专有写法:

bash 复制代码
#!/bin/sh
set -euo pipefail
files=( "$@" )
[[ ${#files[@]} -lt 1 ]] && exit 2
source ./lib.sh
for i in {1..3}; do
  [[ "$i" =~ ^[0-9]+$ ]] && run "$i"
done

参考答案

  • 数组 files=( )、${#files[@]}
  • [[、=~、模式
  • pipefail(dash sh 不行)
  • source → .
  • {1..10}
    应改为 #!/usr/bin/env bash 或逐项 POSIX 化(S13-02)。

对照题

将下列 Bash 改为 POSIX [ / test 风格(不要求可运行,表达即可):

  1. [[ -f "$f" && -r "$f" ]]
  2. (( $# >= 1 ))
  3. [[ "$x" == *.log ]]

参考答案

  1. [ -f "$f" ] && [ -r "$f" ]
  2. [ "$#" -ge 1 ]
  3. case "$x" in *.log) ... ;; esac 或 [ -f "$x" ] 等按语义改写

下一篇预告

S13-02 :《可移植脚本编写清单与 command/printf》--- 实战 checklist、command -v、避免 echo 陷阱,完成 S13 模块。

相关推荐
s6516654969 小时前
IDT和GDT是什么
linux
Android系统攻城狮9 小时前
Linux Gstreamer深度解析之gst_audio_decoder_set_estimate_rate调用流程与实战(四十五)
linux·gstreamer音视频·音视频进阶
FED_AF9 小时前
Linux运维“邪修”功法之通配符
linux·运维
箓维9 小时前
线程的概念和理解
linux
云杂项10 小时前
tmux指南:安装、配置与高效使用(Ubuntu上)
linux·服务器·ubuntu
j7~10 小时前
【Linux网络编程】四十七.《Linux IO 模型详解:阻塞 IO、非阻塞 IO 与 IO 多路转接(select)》
linux·运维·网络·select·非阻塞io·阻塞io·i/o多路连接
GeW10 小时前
智能工厂背后:Linux在跑,数据库在扛,高端制造在加速
linux
qq_3945620011 小时前
【在Linux上升级nginx】
linux·服务器·nginx
Tairitsu_H11 小时前
[Linux系统] 基础IO核心 | 系统接口 | 文件描述符 | 重定向
linux·服务器·文件操作
嵌入式-老费11 小时前
esp32开发与应用(用Python+PySide6解决上位机开发)
linux·单片机·嵌入式硬件