このスクリプトを使用すると、「print_unicode:line 9:printf:Unicode 0187のUnicode番号がありません」エラーが発生するのはなぜですか?

このスクリプトを使用すると、「print_unicode:line 9:printf:Unicode 0187のUnicode番号がありません」エラーが発生するのはなぜですか?

私の端末でUnicode文字がどのように見えるかを確認するためにこれをシェルスクリプトとして書いています。

#!/bin/bash

X=0

while [ $X -lt 65536 ]; do
    HEX=`bc <<< "obase=16; $X"`
    HEX="0x${HEX}"
    UCODENAME=`printf "%0*x\n" 4 $HEX`
    UCODECHAR=`printf "\u%0*x\n" 4 $HEX`
    echo -e "Unicode ${UCODENAME} = ${UCODECHAR}"
    X=$((X + 1))
done

スクリプトを実行すると、次の出力が表示されます。

print_unicode: line 9: printf: missing unicode digit for \u
Unicode 0188 = ƈ

2行目は私が探しているものです。

printf私はエラーを取り除くためにこの方法を厳密に使用しようとします。

#!/bin/bash

X=0

while [ $X -lt 65536 ]; do
    HEX=`bc <<< "obase=16; $X"`
    HEX="0x${HEX}"
    printf 'Unicode %0*x = \u%0*x\n' 4 $HEX 4 $HEX
    X=$((X + 1))
done

次の結果が表示されます。

print_unicode: line 8: printf: missing unicode digit for \u
Unicode 037f = \u037f

2行目は私が探しているものではなく、同じエラーメッセージが表示されます。

このエラーをどのように解決できますか?

ボーナス:これに対するよりエレガントな解決策は何ですか?

答え1

エラーが発生する理由は次のとおりです。
組み込み printf は、後に実際の数字\U(または)が続く場合にのみエラーを理解します。\u

$ printf '\U0021'
!

数値を生成して変換するには2段階のprintfが必要です(二重引用符を渡すには大きな\が必要です)。

$ printf '%b' "$(printf '\\U%04X' 33)"
!

あなたが望むように:

$ printf '%b' "$(printf '\\u%0*X' 4 33)"
!

これはまた働きます:

$ printf '%b' "$(printf '\\U%0*X' 8 33)"
!

bashに16進数を伝えるためにbcを使用する必要はありません。
bashはこれをよく理解しています:

$ a=$(( 0xdef )); echo $(( a + 1 ))
3568

数値の16進値を取得するだけでprintf十分です。

$ printf '0x%06x' 3568
0x000df0

このループは次のように単純化できます。

#!/bin/bash

cp=$((0x020))     len=6

for (( cp=32; cp<$((0x010000)); cp++)); do    
    Ucode="$(printf '%b' "$(printf '\\U%0*X' "$len" "$cp")")"
    printf 'Unicode U%0*x = %s\n' 4 "$cp" "$Ucode"
done

警戒する0x20から0x010000(〜64kライン)まで多くのラインがあります。

UNICODEのコードポイントは最大10FFFFなので、lenを6に増やしました。

もちろん、Ucodeの完全な定義は次のとおりです。

Ucode="$(printf '%b' "$(printf '\\U%0*X' $len "$cp")")"

cpdec = 32またはHEX = 0x20の下のコードポイント()は制御文字です。

コードがこのようなコードポイントで動作していても、私はそうします。いいえ彼らと遊ぶことをお勧めします。

とは別にUNICODE U0000の場合、値が変数に割り当てられるためです。

この印刷\0

$ printf '%b' "$(printf '\\U%0*X' "6" "0")"

xxdで確認してください:

$ printf '%b' "$(printf '\\U%0*X' "6" "0")" | xxd
0000000: 00

警告する:4.3以下のBashはutf-8からU0080とU00FFの間の値を正しくエンコードできません。バージョン4.3または4.4を使用してください。

答え2

私は努力し続け、解決策を見つけました。

#!/bin/bash

X=0

while [ $X -lt 65536 ]; do
    HEX=`bc <<< "obase=16; $X"`
    HEX="0x${HEX}"
    UCODE=`printf "%0*x\n" 4 $HEX`
    printf "Unicode ${UCODE} = \u${UCODE}\n"
    X=$((X + 1))
done                                                        

私はこのようにprintfを試してみました。 https://stackoverflow.com/questions/5947742/how-to-change-the-output-color-of-echo-in-linux

私はまだもう少しエレガントな解決策を見たいです。

答え3

他の方法でこれを行うことができます(bashはuエスケープされたバックスラッシュを無視しているようです"\u")。

#!/bin/bash

X=0

while [ $X -lt 65536 ]; do
    HEX=$(bc <<< "obase=16; $X")
    HEX="0x${HEX}"
    UCODENAME=$(printf "%0*x\n" 4 $HEX)
    UCODECHAR="\\u$(printf "%0*x" 4 $HEX)"
    echo -e "Unicode ${UCODENAME} = ${UCODECHAR}"
    X=$((X + 1))
done

もちろん、スクリプトはまだbashにのみ適用されます。その他のコメント:

  • ほとんどの人はバックティックではなく$(andを使用することをお勧めします。)
  • bashはprintfUnicodeを直接印刷できます(エコーは必要ありません)。
  • 追加printf項目はUCODECHAR重複しています。

重複排除:

#!/bin/bash

X=0

while [ $X -lt 65536 ]; do
    HEX=$(bc <<< "obase=16; $X")
    HEX="0x${HEX}"
    UCODENAME=$(printf "%0*x\n" 4 $HEX)
    UCODECHAR="\\u${UCODENAME}"
    echo -e "Unicode ${UCODENAME} = ${UCODECHAR}"
    X=$((X + 1))
done

関連情報