如果我执行以下简单脚本:
#!/bin/bash
printf "%-20s %s\n" "Früchte und Gemüse" "foo"
printf "%-20s %s\n" "Milchprodukte" "bar"
printf "%-20s %s\n" "12345678901234567890" "baz"
它打印:
Früchte und Gemüse foo
Milchprodukte bar
12345678901234567890 baz
也就是说,带有变音符号(例如ü
)的文本会被每个变音符号“缩小”一个字符。
当然,我在某个地方有一些错误的设置,但是我无法弄清楚可能是哪一个。
如果文件的编码为UTF-8,则会发生这种情况。
如果我将其编码更改为latin-1,则对齐是正确的,但是变音符号却被错误地呈现:
Fr�chte und Gem�se foo
Milchprodukte bar
12345678901234567890 baz
POSIX需要 printf
的%-20s
计算这20来讲字节不是字符,即使是没有什么意义,因为printf
是打印文本,格式化(见讨论在奥斯汀集团(POSIX)和bash
邮件列表)。
POSIX shell和其他大多数POSIX shell的printf
内置组件都可以bash
证明这一点。
zsh
忽略了这个愚蠢的要求(即使在sh
仿真中也是如此),因此printf
可以按您期望的那样工作。printf
内建的相同fish
(不是类似POSIX的shell)。
ü
以UTF-8编码时,字符(U + 00FC)由两个字节(0xc3和0xbc)组成,这说明了差异。
$ printf %s 'Früchte und Gemüse' | wc -mcL
18 20 18
该字符串由18个字符组成,宽18列(-L
是GNUwc
扩展,用于报告输入中最宽行的显示宽度),但编码为20个字节。
在zsh
或中fish
,文本将正确对齐。
现在,有些字符的宽度为0(例如,组合字符,如U + 0308,组合偏斜),或者像许多亚洲脚本中一样,其宽度为双倍(更不用说Tab等控制字符了),甚至zsh
无法对齐那些正确的。
例如,在zsh
:
$ printf '%3s|\n' u ü $'u\u308' $'\u1100'
u|
ü|
ü|
ᄀ|
在bash
:
$ printf '%3s|\n' u ü $'u\u308' $'\u1100'
u|
ü|
ü|
ᄀ|
ksh93
具有一种%Ls
格式说明,可以根据显示宽度来计算宽度。
$ printf '%3Ls|\n' u ü $'u\u308' $'\u1100'
u|
ü|
ü|
ᄀ|
如果文本包含TAB之类的控制字符,这仍然行不通(怎么可能?printf
必须知道制表位在输出设备中的距离以及其开始打印的位置)。它确实偶然地使用了退格字符(例如在roff
输出中,X
(粗体X
)写为X\bX
),尽管ksh93
考虑到所有控制字符的宽度都为-1
。
在中zsh
,您可以使用其填充参数扩展标志(l
用于左填充,r
用于右填充),当与该m
标志结合使用时,将考虑字符的显示宽度(而不是字符串中的字符数):
$ () { printf '%s|\n' "${(ml[3])@}"; } u ü $'u\u308' $'\u1100'
u|
ü|
ü|
ᄀ|
与expand
:
printf '%s\t|\n' u ü $'u\u308' $'\u1100' | expand -t3
这适用于某些expand
实现(尽管不是GNU的)。
在GNU系统中,你可以使用GNUawk
其printf
计数字符(不是字节,而不是显示宽度,所以仍然为0,宽度或2角字符也不行,但确定为您的样品):
gawk 'BEGIN {for (i = 1; i < ARGC; i++) printf "%-3s|\n", ARGV[i]}
' u ü $'u\u308' $'\u1100'
如果输出到终端,则还可以使用光标定位转义序列。喜欢:
forward21=$(tput cuf 21)
printf '%s\r%s%s\n' \
"Früchte und Gemüse" "$forward21" "foo" \
"Milchprodukte" "$forward21" "bar" \
"12345678901234567890" "$forward21" "baz"
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句