web-dev-qa-db-ja.com

awkまたはsedで16進数を10進数に変換する

カンマ区切りの数値のリストがあります。

123711184642,02,3583090366663629,639f02012437d4
123715942138,01,3538710295145500,639f02afd6c643
123711616258,02,3548370476972758,639f0200485732

次のように、3番目の列を3つに分割する必要があります。

123711184642,02,3583090366663629,639f02,0124,37d4
123715942138,01,3538710295145500,639f02,afd6,c643
123711616258,02,3548370476972758,639f02,0048,5732

最後の2つの列の数字を10進数に変換します。

123711184642,02,3583090366663629,639f02,292,14292
123715942138,01,3538710295145500,639f02,45014,50755
123711616258,02,3548370476972758,639f02,72,22322
20
bernie

ここにジョナサンの答えのバリエーションがあります:

_awk $([[ $(awk --version) = GNU* ]] && echo --non-decimal-data) -F, '
    BEGIN {OFS = FS}
    {
        $6 = sprintf("%d", "0x" substr($4, 11, 4))
        $5 = sprintf("%d", "0x" substr($4,  7, 4))
        $4 = substr($4,  1, 6)
        print
    }'
_

必要に応じて -non-decimal-data オプションを追加するかなり歪んだ方法を含めました。

編集

簡単に言うと、これが純粋なBashの同等機能です。

_saveIFS=$IFS
IFS=,
while read -r -a line
do
    printf '%s,%s,%d,%d\n' "${line[*]:0:3}" "${line[3]:0:6}" "0x${line[3]:6:4}" "0x${line[3]:10:4}"
done
IFS=$saveIFS
_

_"${line[*]:0:3}"_(引用符付き__*_)は、AWKのOFSと同様に機能し、BashのIFS(ここではコンマ)が出力の配列要素の間に挿入されます。以下のように配列要素を挿入することで、この機能をさらに活用できます。これは、上記のAWKバージョンとより密接に対応しています。

_saveIFS=$IFS
IFS=,
while read -r -a line
do
    line[6]=$(printf '%d' "0x${line[3]:10:4}")
    line[5]=$(printf '%d' "0x${line[3]:6:4}")
    line[4]=$(printf '%s' "${line[3]:0:6}")
    printf '%s\n' "${line[*]}"
done
IFS=$saveIFS
_

残念ながら、Bashでは_printf -v_(sprintf()に類似)が配列要素に代入することを許可していないため、_printf -v "line[6]" ..._は機能しません。

編集:Bash 4.1以降、_printf -v_が配列要素に割り当てられるようになりました。例:

_printf -v 'line[6]' '%d' "0x${line[3]:10:4}"
_

配列参照を囲む引用符は、ファイル名の一致を防ぐために必要です。 「line6」という名前のファイルが現在のディレクトリに存在し、参照が引用されていない場合、_line6_という名前の変数が作成(または更新)され、printf出力が含まれます。ファイルの内容など、ファイルに関する他のことは何も起こりません。名前のみ-接線のみ。

23

これはうまくいくようです:

_awk -F, '{ p1 =       substr($4,  1, 6);
           p2 = ("0x" substr($4,  7, 4)) + 0;
           p3 = ("0x" substr($4, 11, 4)) + 0;
           printf "%s,%s,%s,%s,%d,%d\n", $1, $2, $3, p1, p2, p3;
         }'
_

サンプル入力データの場合、以下が生成されます。

_123711184642,02,3583090366663629,639f02,292,14292
123715942138,01,3538710295145500,639f02,45014,50755
123711616258,02,3548370476972758,639f02,72,22322
_

「0x」と4桁の16進数の文字列連結に0を追加すると、awkは数値を16進数として扱います。

これを簡略化して、次のことができます。

_awk -F, '{ p1 =      substr($4,  1, 6);
           p2 = "0x" substr($4,  7, 4);
           p3 = "0x" substr($4, 11, 4);
           printf "%s,%s,%s,%s,%d,%d\n", $1, $2, $3, p1, p2, p3;
         }'
_

0xで始まる文字列は、printf()および_%d_形式に渡されると、整数に強制されます。


上記のコードは、MacOS X 10.6.5(バージョン20070501)上のネイティブawkで問題なく動作します。悲しいことに、これはGNU gawk 3.1.7で動作しません。それは、POSIXに従って動作が許可されているようです(以下のコメントを参照)。ただし、gawkには非標準の関数strtonumがあり、これを使って正しく実行することができます-ブラッジングが必要です。

_gawk -F, '{ p1 =      substr($4,  1, 6);
            p2 = "0x" substr($4,  7, 4);
            p3 = "0x" substr($4, 11, 4);
            printf "%s,%s,%s,%s,%d,%d\n", $1, $2, $3, p1, strtonum(p2), strtonum(p3);
          }'
_
9

AWKによる

この回答は、awkによる変換を移植性のある方法で行う方法を示すことに集中しています。

Gawkに_--non-decimal-data_を使用することは GNU Awkユーザーガイド に従って推奨されていません。また、strtonum()の使用は移植できません。

次の例では、各レコードの最初のワードが変換されます。

ユーザー定義関数

変換を行う最もポータブルな方法は、ユーザー定義のawk関数[ 参照 ]によるものです。

_function parsehex(V,OUT)
{
    if(V ~ /^0x/)  V=substr(V,3);

    for(N=1; N<=length(V); N++)
        OUT=(OUT*16) + H[substr(V, N, 1)]

    return(OUT)
}

BEGIN { for(N=0; N<16; N++)
        {  H[sprintf("%x",N)]=N; H[sprintf("%X",N)]=N } }

{ print parsehex($1) }
_

シェルのprintfを呼び出すことにより

あなたはこれを使うことができます

_awk '{cmd="printf %d 0x" $1; cmd | getline decimal; close(cmd); print decimal}'
_

しかし、それは比較的遅いです。変換する改行で区切られた16進数が多数ある場合、次の方が高速です。

_awk 'BEGIN{cmd="printf \"%d\n\""}{cmd=cmd " 0x" $1}END{while ((cmd | getline dec) > 0) { print dec }; close(cmd)}'
_

単一のprintfコマンドに非常に多くの引数を追加すると、問題が発生する可能性があります。

Linuxの場合

私の経験では、以下はLinuxで動作します。

_awk -Wposix '{printf("%d\n","0x" $1)}'
_

Ubuntu Linux 14.04でgawk、mawk、original-awkでテストしました。 original-awkを使用すると、コマンドは警告メッセージを表示しますが、シェルのリダイレクトディレクティブ_2>/dev/null_を使用して非表示にできます。そうしたくない場合は、次のようにoriginal-awkの場合に_-Wposix_を取り除くことができます。

_awk $(awk -Wversion >/dev/null 2>&1 && printf -- "-Wposix") '{printf("%d\n","0x" $1)}'
_

(Bash 4では、_>/dev/null 2>&1_を_&>/dev/null_に置き換えることができます)

注:-Wposixトリックは、おそらくOS Xおよび一部のBSD OSバリアントで使用されるnawkでは機能しません。

7
jarno

これはあなたのために働くかもしれません(GNU sed&printf):

sed -r 's/(....)(....)$/ 0x\1 0x\2/;s/.*/printf "%s,%d,%d" &/e' file

最後の8文字を分割し、フィールドの前に16進数の識別子を付けてスペースを追加してから、printfを使用して行全体を評価します。

0
potong
cat all_info_List.csv| awk 'BEGIN {FS="|"}{print $21}'| awk 'BEGIN {FS=":"}{p1=$1":"$2":"$3":"$4":"$5":";  p2 = strtonum("0x"$6); printf("%s%02X\n",p1,p2+1) }'

上記のコマンドは、フィールド区切り文字が「|」であるファイル「all_info_List.csv」の内容を出力します。次に、フィールド21(MACアドレス)を受け取り、フィールド区切り文字「:」を使用して分割します。変数「p1」に各MACアドレスの最初の5バイトを割り当てるため、このMACアドレスがある場合、「11:22:33:44:55:66」の場合、p1は次のようになります。 「11:22:33:44:55:」。 p2には、最後のバイトの10進値が割り当てられます。「0x66」は、10進数の「102」をp2に割り当てます。最後に、printfを使用してp1p2を結合し、p2を追加してからhexに変換し直しています。

0
Sophoclis
printf "%d\n", strtonum( "0x"$1 )"
0
user164485