printfが分音符を「縮小」するのはなぜですか?

printfが分音符を「縮小」するのはなぜですか?

次の簡単なスクリプトを実行すると:

#!/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

つまり、発音区別符号付きのテキスト(例:)は、ü発音区別符号ごとに1文字ずつ「縮小」されます。

もちろん、どこかに間違った設定がありますが、どの設定がわかりません。

これは、ファイルエンコーディングがUTF-8の場合に発生します。

エンコーディングをlatin-1に変更すると、ソートは正確ですが、ウムラウトレンダリングエラーが発生します。

Fr�chte und Gem�se   foo
Milchprodukte        bar
12345678901234567890 baz

ベストアンサー1

POSIX必要 printf%-20s20個を数えましょうバイトいいえ数値printf印刷とは関係ありませんが、テキスト、書式設定(議論を参照オースティングループでは(POSIX)とbashメーリングリスト)。

組み込みのprintfPOSIXシェルbashと他のほとんどのPOSIXシェルはそれに従います。

zshこの愚かな要件(シミュレーションでもsh)を無視すると、期待printfどおりに機能します。printf組み込みと同じですfish(POSIXに似たシェルではありません)。

UTF-8でエンコードされると、文字ü(U + 00FC)は2バイト(0xc3と0xbc)で構成され、これらの違いを説明します。

$ printf %s 'Früchte und Gemüse' | wc -mcL
    18      20      18

文字列は18文字で構成され、幅は18列(入力で最も広い行の表示幅を報告するための-LGNU拡張)ですが、20バイトにエンコードされています。wc

zshまたは では、fishテキストが正しくソートされます。

幅0の文字(U + 0308などの文字の組み合わせ、分音記号の結合など)やアジアの多くのスクリプト(Tabなどの制御文字は言うまでもありません)のように、幅が2倍の文字もあり、正しく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システムでは、awkGNUの文字数を文字単位で使用できます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"

おすすめ記事