【问题标题】:bash - How to align text with printf into columns with non-ascii characters?bash - 如何将带有 printf 的文本对齐到具有非 ascii 字符的列中?
【发布时间】:2020-12-20 06:40:38
【问题描述】:

如何使 printf 将值对齐到列中,即使是非 ascii 字符?

以下命令应将第二个和第三个参数中的值对齐到固定的 20 个字符宽度:

printf "%-20s %-20s\n" "Foo" "Bar"
printf "%-20s %-20s\n" "Fooo" "Bar"
printf "%-20s %-20s\n" "Foooó" "Bar"
printf "%-20s %-20s\n" "Foooóó" "Bar"

我希望打印以下内容:

Foo                  Bar
Fooo                 Bar
Foooó                Bar
Foooóó               Bar

实际上,这是打印出来的:

Foo                  Bar
Fooo                 Bar
Foooó               Bar
Foooóó             Bar

由于某种原因,任何非 ascii 字符(例如我的示例中的 ó)都会将列缩短 1 个字符。

【问题讨论】:

  • 你用的是什么编码?
  • @user1934428 我使用 UTF8 重现了这个问题。
  • @adamsfamily :这似乎是不幸的,但printf 中的supposed behaviour
  • 谢谢@user1934428 - 这真的很不幸。 printf 正在打印字符(不是字节),因此它应该计算字符(不是字节)-IMO。
  • @adamsfamily :这也是我的看法。我个人的猜测是 POSIX 的标准化试图保持与旧脚本的向后兼容性,如果你还记得 Unix 的早期(在引入 locale 之前),多字节处理就是其他一切比直截了当。可能,bash printf 只是依赖于 C 编译器中的 printf 函数。

标签: bash printf


【解决方案1】:

另一个解决方案是使用 awk。 awk 的printf 似乎比 bash 的 printf 更能理解 unicode。试试:

$ cat script.sh
#!/bin/sh
awk 'BEGIN{
printf "%-20s %-20s\n", "Foo", "Bar"
printf "%-20s %-20s\n", "Fooo", "Bar"
printf "%-20s %-20s\n", "Foooó", "Bar"
printf "%-20s %-20s\n", "Foooóó", "Bar"
}'

运行时,这会产生对齐的列:

$ sh script.sh
Foo                  Bar                 
Fooo                 Bar                 
Foooó                Bar                 
Foooóó               Bar       

【讨论】:

  • 不幸的是,它根本不适合我。产生与以前相同的输出。请看这个:pastebin.com/kudGnZZN(在 macOS 上运行)
  • @adamsfamily 好的。我知道了。在 MacOS 上,您有 BSD awk,它与我正在使用的 awk (GNU awk) 完全不同。您可以通过自制软件:brew install gawk 安装名为 gawk 的 GNU awk 来实现此功能。我没有 Mac,所以很抱歉我无法提供更多帮助。
【解决方案2】:

非 ASCII 字符,例如ó 不像单宽字符的 ASCII 那样打印。

一种解决方案是在printf 的两个字符串之间使用tab 字符并将输出通过管道传输到column

prnt() {
    printf "%-20s\t%-20s\n" "Foo" "Bar"
    printf "%-20s\t%-20s\n" "Fooo" "Bar"
    printf "%-20s\t%-20s\n" "Foooo" "Bar"
    printf "%-20s\t%-20s\n" "Fooooo" "Bar"
    printf "%-20s\t%-20s\n" "Foooooo" "Bar"
    printf "%-20s\t%-20s\n" "Fooooooo" "Bar"
    printf "%-20s\t%-20s\n" "Foooooooo" "Bar"
    printf "%-20s\t%-20s\n" "Fooooooooo" "Bar"
    printf "%-20s\t%-20s\n" "Foooooooooo" "Bar"
    printf "%-20s\t%-20s\n" "Fóó" "Bar"
    printf "%-20s\t%-20s\n" "Fóóó" "Bar"
    printf "%-20s\t%-20s\n" "Fóóóó" "Bar"
    printf "%-20s\t%-20s\n" "Fóóóóó" "Bar"
    printf "%-20s\t%-20s\n" "Fóóóóóó" "Bar"
    printf "%-20s\t%-20s\n" "Fóóóóóóó" "Bar"
    printf "%-20s\t%-20s\n" "Fóóóóóóóó" "Bar"
    printf "%-20s\t%-20s\n" "Fóóóóóóóóó" "Bar"
    printf "%-20s\t%-20s\n" "Fóóóóóóóóóó" "Bar"
}
 
prnt | column -t -s $'\t'

Online Demo

输出:

Foo                   Bar                 
Fooo                  Bar                 
Foooo                 Bar                 
Fooooo                Bar                 
Foooooo               Bar                 
Fooooooo              Bar                 
Foooooooo             Bar                 
Fooooooooo            Bar                 
Foooooooooo           Bar                 
Fóó                   Bar                 
Fóóó                  Bar                 
Fóóóó                 Bar                 
Fóóóóó                Bar                 
Fóóóóóó               Bar                 
Fóóóóóóó              Bar                 
Fóóóóóóóó             Bar                 
Fóóóóóóóóó            Bar                 
Fóóóóóóóóóó           Bar                 

【讨论】:

  • 问题是这不能扩展。这仅在 utf8 字符数小于制表符大小时假装有效。试试这个:pastebin.com/uSzd2MWq
  • @adamsfamily:请检查更新的答案和链接的演示。我们只需要将printf 的输出通过管道传输到column 命令。
猜你喜欢
  • 2019-11-22
  • 1970-01-01
  • 2016-01-04
  • 1970-01-01
  • 1970-01-01
  • 2010-12-30
  • 2020-12-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多