gawk で 16 進数の列を 10 進数に変換する方法、gawk の strtonum が誤った結果を返す

gawk で 16 進数の列を 10 進数に変換する方法、gawk の strtonum が誤った結果を返す

ネットワーク ストリームからデータベースをスクリプト化しようとしています。ネットワーク ストリームは、大量の sed を実行した後、次のような 3 列のファイル (file.db など) をドロップします。

123.123.123.123、コンピュータ名、110000103e21cc4

123.123.123.124、コンピュータ2、11000010416200f

123.123.123.1、コンピュータ3、110000106eb3f43

gawkコマンドを試してみましたが、効果はありませんでした

gawk 'BEGIN {FS=OFS=","} {print $1,$2,strtonum("0x"$3)}' file.db

上記の出力は次のようになります

123.123.123.123、コンピュータ名、76561198025415874

123.123.123.124、コンピュータ2、76561198028824592

123.123.123.1、コンピュータ3、76561198076346171

ただし、出力は次のように変換されるはずです

123.123.123.123、コンピュータ名、76561198025415876

123.123.123.124、コンピュータ2、76561198028824591

123.123.123.1、コンピュータ3、76561198076346179

出力は常にわずかにずれているので、システム上のライブラリが正しくないのではないかと考えています...ちなみに、これは組み込みシステムで実行されており、bc、printfなどで実行したので変換できることはわかっています。

どうすればこれを機能させることができるか

答え1

内部的には、gawk変換された値が倍精度浮動小数点数として保存されるため、わずかな差異は、あらゆる浮動小数点数に固有の丸め誤差にすぎません。正確な結果を得るには、gawkなどの任意精度の数値をサポートする他のコマンドに数値処理をアウトソースする必要がありますbc

しかし、現在のgawk構文では gawk 内で複雑なシェル コマンドライン解析を行うことは不可能なので、まずはシェル スクリプト ヘルパーが必要になります。次のように名前を付けましょうbc.sh

#!/bin/bash
echo -e "ibase=16\n$1" | bc -q

このスクリプトはibase=16、最初の引数 (16 進数) を に入力しbcbc対応する 10 進数を出力します。次に、 はgawk次のように呼び出されます。

gawk 'BEGIN {FS=OFS=","} { "./bc.sh " toupper($3) | getline b; print $1,$2,b}' file.db

これは、gawk大文字の $3 (bc小文字の 16 進値はサポートされていません) を使用してシェル スクリプトを呼び出し、結果をb変数に格納し、すべての引数を一度に出力するように指示します。

./bc.sh二重引用符内にスペースを追加する必要があることに注意してください。そうしないと、 などの存在しないファイルを実行しようとします./bc.sh110000103E21CC4

答え2

振り返ってみると、私が最終的にこうなったのは

convert12345678.sh のような bash スクリプトを作成する

#!/opt/bin/bash
(echo -e "ibase=16\nobase=0A" ; echo $1 | tr 'a-z' 'A-Z') | bc | tr "\n" " " | sed 's/\ //g'

そして、私がOPで言ったように、gawkで必要なことはすべて(それ以来そのプログラムを大幅に変更しました)次のようにしました。このプログラムをパイプしましたが、ファイルからデモンストレーションします。

gawk -F, '{printf("%s,%s,",$1,$2)};{system("/files/convert12345678 "$3)};{printf("\n")}' file.db

私は、bash スクリプトの改行を削除してこの方法を実行しました。なぜなら、後でスクリプトの実行場所を移動したからです。そのため、この方法では、printf で変更したい場合を除き、変換直後に出力に改行が挿入されることはありません。

関連情報