Pig 0.9より前のバージョンでマクロもどきを実現する2つのスクリプト

みなさんPig 0.9って使ってます?個人的にPig 0.9の最大の魅力はマクロのサポートだと思います。
これによって、今まで同じ処理を記述するためにコピペしたり(さらにはエイリアス名を少しいじったり)、Pigを生成するためのスクリプトを書いたりしなければいけなかったのがグッと楽になります。
メンテナンス性も飛躍的に上がりますね!!

でも諸事情があってPig 0.9に移行できない場合もありますよね?
そんな時のために次のような2つの単純なスクリプトを用意しておくと幸せになれるかもしれません。

マクロもどき用Shell Script

・import_pig

#!/bin/sh

pig_script=$1
[ -f "$pig_script" ] || exit 1

cat <<EOF
"eval \\\\"cat <<CMD
\\\\\\\\\\\$(paste -d\\\\\\\\\\\\\\\\0 /dev/null $pig_script | sed 's/\\\\\\\\\\\\\\$\\\\([0-9]\\\\)/\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\$\\\\1/g')
CMD\\\\""
EOF

・exec_pig_macro

#!/bin/sh

macro_name=$1
[ -z "$macro_name" ] && exit 1
shift

eval $@ "$macro_name"

実行権限を与えて/usr/local/binにでも設置します。

$ chmod +x import_pig exec_pig_macro
$ sudo mv import_pig exec_pig_macro /usr/local/bin/

使用例1

次のようにimport_pigコマンドでマクロを記述したファイルを指定して変数に格納し、

%declare macro_name `import_pig macro_file.pig`

exec_pig_macroに先ほど定義した変数を指定します。その後にマクロ内で定義されている変数を指定します。

%declare macro1 `exec_pig_macro $macro_name var1=hoge var2=fuga ... varN=piyo`

最後にexec_pig_macroの結果を呼び出します。

$macro1

試しにドキュメントの例を書いてみます。
ちなみに試した環境はMac OS X 10.6.8、Pig 0.8.1です。

・my_macro.pig

D = LOAD 'data' AS (a0:int, a1:int, a2:int);
$B = FILTER D BY ($1 == 8) OR (NOT ($0+$2 > $1));

・macro_sample1.pig

%declare my_macro `import_pig my_macro.pig`

%declare my_macro1 `exec_pig_macro $my_macro B=X`
$my_macro1
STORE X INTO 'output';

・data

2	3	2
4	8	5
1	2	3

・実行

$ pig -x local macro_sample1.pig

そうするとローカルに次のようなoutput/part-m-00000というファイルが生成されます。
・output/part-m-00000

4	8	5

使用例2

同じくドキュメントの例です。

・group_and_count.pig

D = GROUP $A BY $group_key PARALLEL $reducers;
$B = FOREACH D GENERATE group, COUNT($A);

・macro_sample2.pig

%declare group_and_count `import_pig group_and_count.pig`

X = LOAD 'users' AS (user, age, zip);
%declare group_and_count1 `exec_pig_macro $group_and_count A=X B=Y group_key=user reducers=20`
$group_and_count1

%declare group_and_count2 `exec_pig_macro $group_and_count A=X B=Z group_key=age reducers=30`
$group_and_count2

STORE Y into 'byuser';
STORE Z into 'byage';

・users

1101	20	123-4567
1101	20	234-5678
1102	30	345-6789
1103	20	456-7890
1104	30	567-8901

・実行

$ pig -x local macro_sample2.pig

そうするとローカルに次のようなファイルが生成されます。
・byuser/part-r-00000

1101	2
1102	1
1103	1
1104	1

・byage/part-r-00000

20	3
30	2

仕組み

単純に指定したファイルの変数に指定した値を代入して挿入しているだけです。

$ pig -r macro_sample1.pig
$ cat macro_sample1.pig.substituted



D = LOAD 'data' AS (a0:int, a1:int, a2:int);
X = FILTER D BY ($1 == 8) OR (NOT ($0+$2 > $1));
STORE X INTO 'output';
$ pig -r macro_sample2.pig
$ cat macro_sample2.pig.substituted 


X = LOAD 'users' AS (user, age, zip);

D = GROUP X BY user PARALLEL 20;
Y = FOREACH D GENERATE group, COUNT(X);


D = GROUP X BY age PARALLEL 30;
Z = FOREACH D GENERATE group, COUNT(X);

STORE Y into 'byuser';
STORE Z into 'byage';

ドキュメントにあるようなマクロの中で別のマクロを呼び出すなんてことはできませんが、これでだいぶ便利になるのではないでしょうか?
というわけで、よかったらお使い下さい!

Pigのエスケープ処理は謎

今回Shell Scriptを作成するにあたってPigのエスケープ処理を考慮しないといけなかったんですが、結局規則性がよくわからず試行錯誤を繰り返して作成することになりました・・・

・substitute_test.pig

%declare user1 `echo \$USER`             --> echo $USER
%declare user2 `echo \\$USER`            --> ERORR!
%declare user3 `echo \\\$USER`           --> echo $USER
%declare user4 `echo \\\\$USER`          --> echo \$USER
%declare user5 `echo \\\\\$USER`          --> echo \\$USER
%declare user6 `echo \\\\\\$USER`        --> ERORR!
%declare user7 `echo \\\\\\\$USER`       --> ERORR!
%declare user8 `echo \\\\\\\\$USER`      --> echo \$USER
%declare user9 `echo \\\\\\\\\$USER`     --> echo \\\\$USER
%declare user10 `echo \\\\\\\\\\$USER`   --> ERROR!
%declare user11 `echo \\\\\\\\\\\$USER`  --> echo \\\\$USER
%declare user12 `echo \\\\\\\\\\\\$USER` --> echo \\\$USER
%declare user13 '\$USER'                 --> ERROR!
%declare user14 '\\$USER'                --> $USER
%declare user15 '\\\$USER'               --> ERROR!
%declare user16 '\\\\$USER'              --> $USER
%declare user17 '\\\\\$USER'             --> ERROR!
%declare user18 '\\\\\\$USER'            --> \$USER
%declare backslash1 '\ '
%declare backslash2 '\\ '
%declare backslash3 '\\\ '
%declare backslash4 '\\\\ '
%declare backslash5 '\\\\\ '
%declare backslash6 '\\\\\\ '
%declare backslash7 '\\\\\\\ '
%declare backslash8 '\\\\\\\\ '

$user1 -- arabiki
--$user2
$user3 -- arabiki
$user4 -- $USER
$user5 -- \arabiki
--$user6
--$uesr7
$user8 -- $USER
$user9 -- \\arabiki
--$user10
$user11 -- \\arabiki
$user12 -- \$USER
--$user13
$user14 -- $USER
--$user15
$user16 -- $USER
--$user17
$user18 -- \$USER
$backslash1 --
$backslash2 -- \
$backslash3 -- \
$backslash4 -- \\
$backslash5 -- \\
$backslash6 -- \\\
$backslash7 -- \\\
$backslash8 -- \\\\
$ pig -r substitute_test.pig
$ cat substitute_test.pig.substitute
		 --> echo $USER
		 --> ERORR!
		 --> echo $USER
		 --> echo \$USER
	 --> echo \\$USER
	 --> ERORR!
	 --> ERORR!
	 --> echo \$USER
	 --> echo \\\\$USER
	 --> ERROR!
	 --> echo \\\\$USER
 --> echo \\\$USER
		 --> ERROR!
		 --> $USER
		 --> ERROR!
		 --> $USER
		 --> ERROR!
		 --> \$USER









arabiki -- arabiki
--$user2
arabiki -- arabiki
$USER -- $USER
\arabiki -- \arabiki
--$user6
--$uesr7
$USER -- $USER
\\arabiki -- \\arabiki
--$user10
\\arabiki -- \\arabiki
\$USER -- \$USER
--$user13
$USER -- $USER
--$user15
$USER -- $USER
--$user17
\$USER -- \$USER
  --
\  -- \
\  -- \
\\  -- \\
\\  -- \\
\\\  -- \\\
\\\  -- \\\
\\\\  -- \\\\

ドルマークとかクォートとか正規表現とかが絡むとわけがわからなくなります・・・