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

如何在Bash中用printf实现含Unicode/终端装饰的字符串对齐?

解决Bash中含Unicode宽字符或颜色代码的文本列对齐问题

核心问题原因

Bash内置的printf "%-Ns"是按字节数计算宽度,而非终端显示的可视列数:

  • 多字节Unicode宽字符(如★)占2字节但终端显示占2列,普通Unicode字符占多字节但显示占1列,printf会误算为字节数。
  • 终端装饰代码(如ANSI颜色序列)是不可见的控制字符,printf会将其计入宽度,导致实际可视文本缩进不足。

完美解决方案(支持颜色代码+Unicode宽字符)

使用Perl的Unicode::GCString模块,该模块可以准确计算字符串的终端可视列数,同时剥离不可见的ANSI控制码。

步骤1:安装依赖

Debian/Ubuntu系:

sudo apt install libunicode-linebreak-perl

RHEL/CentOS系:

sudo yum install perl-Unicode-LineBreak

步骤2:定义对齐函数

在Bash脚本中添加以下函数:

# 计算文本的终端可视列数(剥离ANSI控制码,处理宽字符)
get_visible_length() {
    perl -MUnicode::GCString -e '
        $str = do { local $/; <STDIN> };
        $str =~ s/\e\[[0-9;]*m//g; # 移除所有ANSI SGR控制序列
        $gc = Unicode::GCString->new($str);
        print $gc->columns(), "\n";
    ' <<< "$1"
}

# 左对齐文本到指定宽度
align_left() {
    local text="$1"
    local target_width="$2"
    local actual_length=$(get_visible_length "$text")
    local pad_length=$(( target_width - actual_length ))
    
    # 确保填充长度非负
    if [ $pad_length -lt 0 ]; then
        pad_length=0
    fi
    
    printf "%s%s" "$text" "$(printf "%${pad_length}s" "")"
}

步骤3:测试使用

# 定义颜色输出函数
printred () { tput setaf 1; printf %b "$*"; tput sgr0; }

# 测试对齐效果
align_left "test" 20 ; echo " NOK"
align_left "$(printred RED)" 20 ; echo " NOK"
align_left "★★★★" 20 ; echo " NOK"

输出结果(完美对齐):

test                 NOK
RED                 NOK
★★★★               NOK

轻量化方案(仅支持颜色代码,不处理宽字符)

如果不需要处理Unicode宽字符,可使用sed剥离控制码后用wc计算字符数,无需额外安装模块:

get_visible_length_light() {
    echo -n "$1" | sed 's/\x1b\[[0-9;]*m//g' | wc -m
}

align_left_light() {
    local text="$1"
    local target_width="$2"
    local actual_length=$(get_visible_length_light "$text")
    local pad_length=$(( target_width - actual_length ))
    [ $pad_length -lt 0 ] && pad_length=0
    printf "%s%s" "$text" "$(printf "%${pad_length}s" "")"
}

原理说明

  • get_visible_length函数:先用正则移除所有ANSI颜色控制码(\e\[[0-9;]*m匹配终端样式控制序列),再通过Unicode::GCString的columns()方法获取字符串在终端中实际占用的列数,该方法会自动识别宽字符(如东亚字符、emoji等)的列宽。
  • align_left函数:计算目标宽度与实际可视宽度的差值,补充对应数量的空格,实现左对齐。

内容的提问来源于stack exchange,提问作者Popup

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 11:50:31