You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Bash环境下如何获取当前locale的小数分隔符与千位分隔符

Bash中获取当前Locale的数字分隔符

问题背景

之前做Bash下货币格式化相关需求时,需要确定当前系统locale配置下使用的数字分隔符,过程中碰到了不少locale和数字格式化的兼容问题:二进制计算器bc完全不遵循locale规则,始终用.作为小数点输出,和locale配置不匹配时会直接触发报错:

printf '%.5f\n' $(bc -l <<<'4*a(1)')
3.14159

LANG=de_DE printf '%.5f\n' $(bc -l <<<'4*a(1)')
bash: printf: 3.14159265358979323844: invalid number
3,00000

早期实现方案

最开始查找小数分隔符(也叫基数字符/radix character)的时候,用的是借助time命令输出的取巧方案:

int2amount() {
    local TIMEFORMAT=%U _decsep
    read _decsep < <(eval 'time true' 2>&1)
    _decsep=${_decsep//[0-9]}
    ...
}

这个方案可以正常运行,比如适配bc输出圆周率的函数:

pi() { local TIMEFORMAT=%U _decsep;read _decsep < <(eval 'time true' 2>&1);_decsep=${_decsep//[0-9]};
       local pi=$(bc -l <<<'4*a(1)')
       printf '%.5f\n' ${pi/./$_decsep}
}

pi
3.14159
LANG=de_DE pi
3,14159

而千位分隔符的获取很早就有轻量实现,不需要额外fork进程,系统开销极低:

printf -v ts "%'d" 1111 ; ts=${ts//1}

最开始我打算在脚本开头加初始化逻辑,一次性获取两个分隔符存为全局只读变量:

numericSeparators() {
    local TIMEFORMAT=%U
    read NUM_DEC_SEP < <(eval 'time true' 2>&1)
    NUM_DEC_SEP=${NUM_DEC_SEP//[0-9]}
    printf -v NUM_THO_SEP "%'d" 1111
    NUM_THO_SEP=${NUM_THO_SEP//1}
}
numericSeparators
declare -r NUM_THO_SEP NUM_DEC_SEP
...

但这个方案里取小数分隔符的<(eval 'time true' 2>&1)写法需要创建进程替换,实现过于笨重,我一直想找更简洁轻量的方法,不用额外开进程就能同时拿到两个分隔符。

自答


参考dan的实现思路后,我最终写出了非常简洁高效的版本。

适配bc输出的示例

修正bc输出和locale不匹配问题的圆周率计算函数如下:

pi() {
    local _decsep pi=$(bc -l <<<'4*a(1)')
    printf -v _decsep %.1f 1
    printf '%.5f\n' ${pi/./${_decsep:1:1}}
}

运行效果:

pi
3.14159
LANG=de_DE.UTF-8 pi
3,14159

最终轻量分隔符获取函数

下面的函数可以一次性获取两个分隔符存入全局变量:NUM_THO_SEP存储千位分隔符,NUM_DEC_SEP存储小数分隔符,全程无额外进程fork,开销极低:

numericSeparators() {
    local numtest
    printf -v numtest "%'.1f" 1111
    NUM_THO_SEP=${numtest:1:1}
    NUM_THO_SEP=${NUM_THO_SEP/1}
    NUM_DEC_SEP=${numtest: -2:1}
}
numericSeparators

多Locale测试

我在多个常用locale下做了兼容性测试,测试代码如下:

for loctest in {C,en_US,fr_{CH,FR},de_{CH,DE},it_{CH,IT}}{,.UTF8}  ;do
    LANG=$loctest numericSeparators
    LANG=C printf 'LANG=%-12s thsnd=%-1s \e[2m(%q)\e[0m\e[45G radix=%q\n' \
        "$loctest" "$NUM_THO_SEP"{,} "$NUM_DEC_SEP"
done

测试输出:

LANG=C            thsnd=  ('')               radix=.
LANG=C.UTF8       thsnd=  ('')               radix=.
LANG=en_US        thsnd=, (\,)               radix=.
LANG=en_US.UTF8   thsnd=, (\,)               radix=.
LANG=fr_CH        thsnd=' (\')               radix=.
LANG=fr_CH.UTF8   thsnd=' ($'\342\200\231')  radix=.
LANG=fr_FR        thsnd=�($'\240')          radix=\,
LANG=fr_FR.UTF8   thsnd=  ($'\342\200\257')  radix=\,
LANG=de_CH        thsnd=' (\')               radix=.
LANG=de_CH.UTF8   thsnd=' ($'\342\200\231')  radix=.
LANG=de_DE        thsnd=. (.)                radix=\,
LANG=de_DE.UTF8   thsnd=. (.)                radix=\,
LANG=it_CH        thsnd=' (\')               radix=.
LANG=it_CH.UTF8   thsnd=' ($'\342\200\231')  radix=.
LANG=it_IT        thsnd=. (.)                radix=\,
LANG=it_IT.UTF8   thsnd=. (.)                radix=\,

注:我的终端默认使用UTF-8编码,非UTF-8 locale下的不间断空格($'\240')无法以纯ASCII形式正常打印,因此会显示替换字符�。


内容的提问来源于stack exchange,提问作者F. Hauri - Give Up GitHub

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 16:48:53