LOADING...

加载过慢请开启缓存(浏览器默认开启)

loading

坑坑洼洼的 Bash

最近要写个方便使用 Systemd-nspawn 的脚本,本来以为自己对于脚本编程已经轻车路熟,结果血条差点扣掉一滴血……

遇到的问题

简单文本替换

变量替换和 sed 等只支持正则的全面扑街,我想就他喵的关掉则正的功能啊,所以要简单 one-line coding,只能用吊诡的 xxd 把文本转为 16 进制,然后在正则里头查,如:

echo "[a|b]" | xxd -p

转为 5b617c625d0a,就不会被正则为 a 或者 b 了。

字符串匹配不上,在终端输入可以,写在脚本就不行

这个主要是 \r 字符,不仅看不到,而且终端和脚本行为还不一样……

因为脚本的 \r 会先被解析掉,所以需要特殊处理,在:

tr -d '\r'
sed 's/\r//g'

等都是无效的代码,\r 应该得:

printf '\r'

Bash 的 Bug

Bash 本身也有几十年历史,到现在 5 的版本,本身改变并不是很大,但是 release 版本还会出现 bug,而且修复得极为缓慢,去年的邮件列表有该 issue,很早就有 patch,但是一年多了,反倒在常用发行版出现,没有修复,网络的反馈也少,遇见 pop_var_context: head of shell_variables not 错误的时候,很容易找不到北,详细间:

突然还想说说

工具链

这个并不是 Bash 的 Bug,但却是普遍存在的问题,明明没有系统级别的控制,怎么脚本行为却是不一致的。

因为 Bash/Zsh 等脚本编程对于 sed/awk/grep 依赖过大,虽然他们并不算脚本语言的一块,可惜大多数人会将他们视为一体,所以脚本出错后就显得很诡异,而每种系统使用的都不是很一样:

  • Mac:FreeBSD 版本
  • FreeBSD:FreeBSD 版本
  • Linux:GNU 版本
  • Windows/Cygwin:GNU 版本

而且每种 Linux Shell 的行为并不一致,内建命令也不同,考虑到主流的,大约有:

  • Bash
  • Csh
  • Fish
  • Zsh

这里列出每种 Shell 并不涉他们的渊源,如 Tcsh 派生于 Csh 一样,因为就是同一个 Shell 不同版本,不同配置的行为也是不同的,如 Bash 5.2 在下面行为是不一样的:

  • expand_aliases on
  • expand_aliases off

而版本呢,像 localvar_inherit 特性:

  • Bash 5 支持
  • Bash < 5 不支持

所以单脚本本身的工具链就会出现很多不一致的行为。

语言

Fish 这个不说,因为它的语法脱离了常规 Shell 的语法范畴。常规的缺点很明显:

  • 函数没有返回值,只有返回状态
  • 动态作用域,这个只是就个人来说的,很难编写可维护代码
  • 靠输出来当成值,很容易被多种输出干扰到
  • 对数组和关联数组的支持几乎为零,没法传递,使用繁杂困难
  • 模块化差,没有命名空间,全都是全局,Javascript 以前还能在闭包玩,它是完全不能
  • 复用性差,到处 source,脚本没有被管理到加上全局污染,基本都只能一文到底
  • 语法诡异,不管是在交互或者脚本里头写下的语法
  • ……