web-dev-qa-db-ja.com

BASH印刷問題(printf \\ $(printf '%03o' $ 1))

以下を使用して、bashでintからcharおよびcharからintに変換しました。しかし、私はprintf \\$(printf '%03o' $1)または_printf '%d' "'$1"_の仕組みを理解していません。 printf \\$(printf '%03o' $1)と_printf '%d'_の仕組みを説明してください。

_#!/bin/bash
# chr() - converts decimal value to its ASCII character representation
# ord() - converts ASCII character to its decimal value

chr() {
  printf \\$(printf '%03o' $1)
}

ord() {
  printf '%d' "'$1"
}

ord A
echo
chr 65
echo
_
6
1885

_printf '\101'_ここで_101_は8進数で、その値のバイトを出力します。

ASCII端末に送信されると、AはASCIIの文字65(8進数101)であり、すべてのASCII互換文字セット(最新の文字セットを含む)であるため、Aとしてレンダリングされます。一部のIBMシステムでまだ使用されているEBCDICのもの)。

_printf \\$(printf '%03o' $1)
_

どちらを書いておくべきか:

_printf "\\$(printf '%03o' "$1")"
_

パラメータ展開(_$1_など)、またはコマンド置換($(...))を引用符で囲まないままにすることは、Bourneのようなシェルではsplit + glob演算子であり、ここでは不要です。

  • _printf '%03o' "$1"_は、_$1_の数値を3桁の8進数に変換します
  • printf "\\$(...)"は、その8進数を_\_に追加し(二重引用符内の_\\_は_\_になります)、対応するバイト値を出力するようにprintfに渡します。

これは、文字セットが文字ごとに1バイトのロケール(_iso8859-1_など)でのみ機能するか、マルチバイト文字セットのロケールでは、0〜127の値でのみ機能することに注意してください。

bashでは、

_printf '%d\n' "'A"
_

文字A(または、少なくともmbtowc()から返される値で、GNUシステムでは少なくともUnicodeコードポイント)のUnicodeコードポイントを出力します。

他のいくつかの実装(スタンドアロンGNU printfユーティリティを含む)は、代わりに文字の最初のバイトの値を返します。

AのようなASCII文字やASCIIベースのシステムでは、違いはありませんが、その他の場合は重要です。たとえば、ギリシャ語の_α_文字(U + 03B1)は次のようにエンコードされます。

  • iso8859-7のバイト225(標準のギリシャ語の1バイト文字セット)
  • uTF-8のバイト206 177(Unixのようなシステムで最も一般的に使用されるUnicodeのエンコーディング)
  • gB18030のバイト166 193(公式のUnicodeの中国語エンコーディング)。

Bashの_printf '%d\n' "'α"_は、常に_945_(16進数で0x03b1)を出力します。これは、ロケール(少なくともGNUシステムでは)に関係なく、_α_のUnicodeコードポイントですが、その他ロケールに応じて、225、206、または166を返す場合があります。

これらのchrordは、ASCII文字(または値0〜127)の場合、またはすべての文字(値0〜0)の_iso8859-1_文字セットを使用するロケールでは互いに逆になっていることがわかります。 255)。

ord()がUnicodeコードポイントを返すことを意図している場合、逆(Unicodeコードポイントに対応する文字を出力する)は次のようになります。

_chr() {
  printf "\U$(printf %08X "$1")"
}
_

bash 4.3以上を想定(_\UXXXXXXXX_は4.2で追加されましたが、4.3までの文字U + 0080からU + 00FFまでは正しく機能しませんでした))。

次に、任意のロケールで:

_$ ord α
945
$ chr 945
α
_

または、ord()が(現在のロケールで)指定された文字のエンコーディングのバイトの値を返す場合:

_ord() {
  printf %s "$1" | od -An -vtu1
}
_

そして、chr()がそれらのバイトを出力するために:

_chr() {
  printf "$(printf '\\%o' "$@")"
}
_

次に、たとえばUTF-8ロケールで:

_$ ord α
 206 177
$ chr 206 177
α
_

(あなたの_ord α_は945を与え、あなたのchrは_chr 945_と_chr 206 177_の両方にゴミを与えるでしょう)。

または、_iso8859-7_を使用するロケールで:

_$ ord α
 225
$ chr 225
α
_

(あなたの_ord α_は945を与えますが、GNUシステム上でprintfが_/usr/bin/printf_に置き換えられた場合は225を与える可能性があります)。

9

内側の_printf '%03o' $1_は、_$1_(つまり65)の値を8進値(65-> 101)として返します。

外側のprintf \\$(..)は、8進値で表される文字を出力します。

_man printf_行を参照してください:

8進数NNN(1から3桁)の\ NNNバイト

_printf '%d' "'$1"_の場合、_'_を指定して_$1_を単一の文字定数として扱う必要があることを示します。そうでない場合、printfは値が無効な数値であることを示すエラーをスローします。文字定数の値はprintfによって使用され、10進形式で印刷されます_"%d"_

4
Lambert

chr

printf \\ $(printf '%03o' $ 1) #する必要があります... printf "$(printf '\\%03o' "$1")"

最初のprintfは、2番目の出力を使用して文字を生成します。それを復号化するには、逆方向に作業する必要があります。

2番目のprintfは、整数値($1から)を8進整数('%03o'形式)に変換するために使用されます。

$ printf '%03o' 12
014
$ printf '%03o' 65
101
$ printf '%03o' 255
377

次に、その結​​果は\に相当するものと連結されます(シェルに対するその特別な意味を回避するために、二重にする(または引用する)必要があります)。結果の文字列\101は、printfによってバイトの8進値として解釈されます。ただし、最大3桁(printfのほとんどのシェル実装では)のみです。 3桁の8進数字のみの解釈は、そのタイプのフォーマットが1バイト0-255(または8進数では0-377)で使用されることを意図しているためです。そのため、ほとんどのprintf実装は、8進数を1つだけに変換しますbyte。そのバイトがコンソールで使用されているロケールのASCII文字を表す場合、その文字が出力されます。

$ printf \\101\\n
A
$ printf \\377\\n
�

必要なロケールの影響を受ける文字(バイトではない)を出力するには:

  • 使用中のロケールでマルチバイト文字を生成できるprintf、および
  • バイトではなく文字が欲しいと表現する何らかの形

たとえば、bash(4.3+)では:

$ printf \\U263A\\n
☺

printfは多くの桁を印刷できることに注意してください。

$ printf '%03o\n' 1495195076287004671
122777777777777777777

ただし、最初の3つだけが8進数として受け入れられます。

$ printf '\122777777777777777777'
R777777777777777777                     # Note the R in the front.

ord

printf '%d' "'$1"

データ(形式ではない)文字列は、外側の引用符が削除され、結果の文字列は'で始まります。 'または"で始まる文字列は、printfにとって特別です。それらの文字列について、形式が数値の場合、最初の文字が(数値形式文字列で指定された形式で)出力されます。 :

$ printf '%d\n' '"A'      # decimal (doesn't expand quoted vars '"$var')
65
$ printf '%d\n' "'A"      # decimal
65
$ printf '%o\n' "'A"      # octal
101
$ printf '%x\n' "'A"      # Hexadecimal
41

実装間で"'…"の解釈最初の文字にはいくつかのバリエーションがあることに注意してください(バイトまたは文字。バイトの場合、結果は使用するロケールの影響を受けます)。

0
Isaac