なぜprintfは「縮小」ウムラウトですか?


54

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

#!/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に変更すると、配置は正しくなりますが、ウムラウトは正しくレンダリングされません。

Frchte und Gemse   foo
Milchprodukte        bar
12345678901234567890 baz

14
printfがUTF-8およびその他のマルチバイト文字セットを認識することを期待していますか?
frostschutz

16
文字ではなくバイトをカウントしているように見えます。echo Früchte und Gemüse | wc -c -m違いをご覧ください。
スティーブンキット

7
@frostschutz Zshのprintfです。
スティーブンキット

10
はい、printfが(少なくとも)UTF-8を認識することを期待しています。
ルネナイフェネガー

12
そうではありません。大変な運。;-)
frostschutz

回答:


87

POSIXが必要と printfのの%-20sの面でそれらの20を数えるバイトない文字としてそれはほとんど意味がないにもかかわらず、printf印刷することであるテキストを(議論を参照フォーマットされた、オースティン・グループで(POSIX)とbashメーリングリストを)。

printf組み込みbashや他のほとんどのPOSIXシェルはそれを尊重します。

zsh(そのshエミュレーションであっても)その馬鹿げた要件を無視するので、printf期待どおりに動作します。(POSIXのようなシェルではない)のprintf組み込みについても同じですfish

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

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

この文字列は18文字で構成され、18列幅(入力の最も広い行の表示幅を報告-LするGNU wc拡張機能)ですが、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

他のオプションとして、あなたは試すことができます:

printf '%s\t|\n' u ü $'u\u308' $'\u1100' | expand -t3

これはいくつかのexpand実装で動作します(GNUではありません)。

GNUシステムawkprintfは、char でカウントするGNU を使用できます(バイトではなく、表示幅ではないため、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"

2
それは間違っています。ücaracterは次のように構成することができるu+ ¨3バイトです。質問の場合、2文字としてエンコードされますが、すべてüが同じように作成されるわけではありません。
イスマエルミゲル

6
@IsmaelMiguelは、 1つのグリフ/ graphem / graphem-clusterに対してu\u3082 文字wc -m少なくともUnix /の意味)であり、すでにこの回答に含まれています。
ステファンシャゼラス

「printfはテキストを印刷するのでほとんど意味がありません。」まあ、printfはCの文字(バイト)を扱うと主張することができます。テキストロケールを処理してはならず、(おそらくマルチバイトの)文字セットエンコーディングを理解する負担がありません。しかし、この防衛線は、「%s」バイトの切り捨てが「無効な」テキスト(切り捨てられた文字)にならないという(ISO C99)要件と矛盾します。Glibcはその場合でも失敗します(何も出力しません)。本当の混乱。postgresql.org/message-id/…–
leonbloy

Cさんの感覚になるかもしれない@leonbloy、 printf(3)(ほとんど意味あなたが言及していることC99の要件、そのために感謝した後)ではなく、printf(1)文字ですべてのシェルオペレータまたは他のテキストユーティリティの契約などのユーティリティを(またはように変更されたの文字を扱います以下のようにwcなっている-m(一方で-c滞在バイト)、またはcutそれが得た-b後には-c)バイトより何かを意味するかもしれません。
ステファンシャゼラス

バイトではなく文字を使用したとしても、列の整列には適していません。各文字が占める端末セルの数を知る必要がありますが、これは文字(0〜2)によって異なります。
R ..

10

エンコーディングをlatin-1に変更すると、配置は正しくなりますが、ウムラウトは正しくレンダリングされません。

Frchte und Gemse   foo
Milchprodukte        bar
12345678901234567890 baz

実際、いいえ。ただし、端末はlatin-1を話さないため、ウムラウトではなくジャンクになります。

iconvを使用してこれを修正できます。

printf foo bar | iconv -f ISO8859-1 -t UTF-8

(または、単にiconvにパイプされたシェルスクリプト全体を実行します)


3
これは便利なコメントですが、中心的な質問には答えません。
-gerrit

1
@gerritどうして?printfがlatin1で印刷するときに正しいことをする場合、latin1で印刷し、後でUTF-8に変換しますか?私にとって重要な質問に対する適切な修正のようです。
Wouterヴェルヘルスト

1
核となる質問は「ウムラウトを縮小する理由」であり、答えは(他の答えと同様)「utf-8をサポートしていないため」です。ウムラウトが間違ってレンダリングされる理由、ウムラウトレンダリングを修正する方法を尋ねているわけではありません。いずれにしても、あなたの提案は、iso8859-1(のみ)として表すことができるutf-8のサブセットに役立ちます。
-gerrit

4
@WouterVerhelst、はい。ただし、これはシングルバイト文字セットでエンコードできるテキストにのみ適用できます。
ステファンシャゼル

3
私も質問を「なぜ出力を正しくすることができるか」ではなく、「理由を知っている限り、誤った出力を気にしません」と読みました。
ミスターリスター
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.