awk で BASE64 エンコード

前のAWK Users JP :: awk で BASIC 認証AWK Users JP :: 実用! awk でコマンドラインで Twitter 受信AWK Users JP :: 実用! awk でコマンドラインから Twitter 投稿などでは BASE64 にエンコードするのに base64 コマンドを用いていましたが、gawk の機能を用いて BASE64 エンコードを awk 単独で行うことができます。

The gawk program encodes a binary file to base64 format - Clouder::BloggerAWK script to encode argument to BASE64 (デコードは AWK script to decode argument from BASE64 to ASCII) のように既に awk を使って BASE64 を扱うことを紹介されている方もいます。 ここではThe gawk program encodes a binary file to base64 format - Clouder::Blogger に紹介されているものを関数化して使いやすくしておきます。

関数化を行う際には、関数内で使われる変数をできるだけローカル変数にしておくことが必要になります。 そうしておかないと、awk は基本的にグローバル変数のみですから他で変数が使われてしまう危険があります。

また、ここでは __test__ 変数を用意し、__test__ が真である場合にのみ単独で動作させた際に結果を出力し、それ以外で用いられた場合には関数部のみが用いられるようにしてあります。

#! /usr/bin/gawk -f
# base64.awk

BEGIN {
    if (__test__) {
        str = ARGV[1] ? ARGV[1] : "awk-users-jp";
        print str2base64(str);
    }
}

function str2base64(str,
                    BASE64, result, asc, byte1, byte2, byte3,
                    base1, base2, base3, base4) {

    BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    result = "";

    for (i = 0; i < 256; i++){
        asc[sprintf("%c", i)] = i;
    }

    while (length(str) > 0) {
        # Specify byte values
        if (length(str) == 1){
            byte1 = asc[substr(str, 1, 1)];
            byte2 = 0;
            byte3 = 0;
        }
        if (length(str) == 2){
            byte1 = asc[substr(str, 1, 1)];
            byte2 = asc[substr(str, 2, 1)];
            byte3 = 0;
        }
        if (length(str) >= 3){
            byte1 = asc[substr(str, 1, 1)];
            byte2 = asc[substr(str, 2, 1)];
            byte3 = asc[substr(str, 3, 1)];
        }

        # Create BASE64 values
        base1 = rshift(byte1, 2);
        base2 = lshift(and(byte1, 3), 4) + rshift(and(byte2, 240), 4);
        base3 = lshift(and(byte2, 15), 2) + rshift(and(byte3, 192), 6);
        base4 = and(byte3, 63);

        # Compose BASE64 string
        if (length(str) == 1){
            result = result substr(BASE64, base1 + 1, 1);
            result = result substr(BASE64, base2 + 1, 1);
            result = result "==";
            str = "";
        }
        if (length(str) == 2){
            result = result substr(BASE64, base1 + 1, 1);
            result = result substr(BASE64, base2 + 1, 1);
            result = result substr(BASE64, base3 + 1, 1);
            result = result "=";
            str = "";
        }
        if (length(str) >= 3){
            result = result substr(BASE64, base1 + 1, 1);
            result = result substr(BASE64, base2 + 1, 1);
            result = result substr(BASE64, base3 + 1, 1);
            result = result substr(BASE64, base4 + 1, 1);
            str = substr(str, 4);
        }
    }
    return result;
}

base64 コマンドを使ったものと比較してみましょう。

$ gawk -v __test__=1 -f base64.awk 'awk-users-jp'
YXdrLXVzZXJzLWpw

$ echo -n 'awk-users-jp' | base64
YXdrLXVzZXJzLWpw

同じ出力結果になりました。

tag_gawk.pngtag_gawk.png