unoh.github.com

シェルスクリプトでプログラミング

Wed Jul 16 23:20:11 -0700 2008


パワプロよりパワポケが好きなbokkoです。でも最近はPSPで遊んでいます。


今回はシェルスクリプトやコマンドラインでよく使うプログラムとその使用例の紹介です。

シェルスクリプトでプログラミング



シェルスクリプトでは既にあるコマンドを組み合わせてプログラムを書きます。しかし、シェルスクリプトは分岐や繰り返しといった制御構造を持ち、関数や変数を扱ったり、四則演算を行うこともできます。

演算



シェルスクリプトで演算を行うにはexprを使います。

add.sh

#!/bin/sh
A=1
B=2
C=`expr ${A} + ${B}`
echo ${C}


↑のadd.shを実行すると、

$ sh test.sh
3
$


と表示されます。↓のようにexprの引数をスペースで区切らず渡してしまうと、単に1+2と表示されてしまいますので、注意しましょう。

#!/bin/sh
A=1
B=2
C=`expr ${A}+${B}`
echo ${C}


浮動小数演算



exprでは整数演算しかできません。シェルスクリプトで浮動小数を扱うにはbcコマンドを使います。

compressibility.sh

#!/bin/sh
SRC_SIZE=100
COMPRESSED_SIZE=15
COMPRESSIBILITY=`echo "${SRC_SIZE} / ${COMPRESSED_SIZE}" | bc -l`
echo ${COMPRESSIBILITY}"%"


$ sh compressibility.sh
6.66666666666666666666%
$


有効桁数を指定するにはscale変数を使います。

#!/bin/sh
SRC_SIZE=100
COMPRESSED_SIZE=15
COMPRESSIBILITY=`echo "scale=2;${SRC_SIZE} / ${COMPRESSED_SIZE}" | bc -l `
echo ${COMPRESSIBILITY}"%"


$ sh compressibility.sh
6.66%
$


分岐



if文の使い方は以下のような感じです。/home/bokko以下にtest.txtが存在していれば、そのファイルが削除されます。

#!/bin/sh
TEST_FILE=/home/bokko/test.txt
if [ -e ${TEST_FILE} ]
then
    rm ${TEST_FILE}
    echo "removed "${TEST_FILE}
else
    echo ${TEST_FILE}" is not existed."
fi


繰り返し



繰り返しにはほかの言語でもよく見られるfor文が使用できますが、書き方はif文と同様、少し独特な感じです。

#!bin/sh
for srcname in `find /home/bokko/programming/c | egrep '.+\.c$'`;do
    case `basename $srcname` in
        a.c | b.c | c.c) ;; # exclude
	*)
	    # C言語のソースファイルに何かする
    esac
done


↑のシェルスクリプトを実行すると、/home/bokko/programinng/c以下にあるC言語のソースファイルに対して「# C言語のソースファイルに何かする」の部分に書かれた処理を実行することができます。「#exlude」のコメントアウトがある行はその名の通り、指定したa.c、b.c、c.cを処理の対象から外しています。

パイプ



シェルスクリプトでプログラムを書いたり、普通にコマンドラインで作業していると、あるプログラムの出力を別のプログラムへの入力として扱いたいことがあります。このような複数のプログラム間でのデータの橋渡しをするのがパイプです。一見複雑そうな問題でも個々の小さいプログラムをパイプで繋ぐことによって簡単に解くことができる場合があります。


例えば、以下のようなファイルから重複した行を取り除きたいとします(いい加減な例ですみません)。

bokko.txt

bokko
bokkko
bokkkko
bokko
bokkko
bokko
bokko
bokko
bokkko
bokkko
bokkko
bokkko
bokkko
bokko
bokkko
bokko
bokko
bokkkko
bokkko
bokkko
bokko
bokko
bokkkko
bokko


これは↓のようにsortとuniqをパイプで繋げるだけでできます。

$ sort bokko.txt | uniq
bokkkko
bokkko
bokko
$


各行がいくつ重複しているか調べるにはuniqの-cオプションを使います。

$ sort bokko.txt | uniq -c
      3 bokkkko
     10 bokkko
     11 bokko
$


非常に簡単な例ではありますが、sortとuniqはテキストデータの集計や解析をする際に非常に役に立ちます。

sed



sedはテキストデータを置換するのによく使われるプログラムです。例えば、先程のbokko.txtの全ての行をbokkoに置換してみましょう。

$ cat bokko.txt | sed -e 's/bok\+o/bokko/g' | uniq
bokko
$


という具合に文字列を正規表現を使って簡単に置換することができます。

awk



awkはsedと同じようにテキストを置換したり、整形するためのプログラムです。sedに比べると正規表現の機能が少し弱いですが、テキストの整形機能のほかに四則演算、連想配列、などの機能が備わっています。

sortとuniqの例に比べるとやや冗長ですが、以下のようなawkスクリプトを用意して重複行を数えることができます。

sort_uniq.awk

BEGIN {
    cnt["bokko"] = 0;
    cnt["bokkko"] = 0;
    cnt["bokkkko"] = 0;
}
/^bokko$/ {
    cnt["bokko"]++;
}
/^bokkko$/ {
    cnt["bokkko"]++;
}
/^bokkkko$/ {
    cnt["bokkkko"]++;
}
END {
    print "bokko:" cnt["bokko"];
    print "bokkko:" cnt["bokkko"];
    print "bokkkko:" cnt["bokkkko"];
}


$ cat bokko.txt | awk -f sort_uniq.awk
bokko:11
bokkko:10
bokkkko:3
$


ほかにも、テキストから必要な部分だけを取り出すといったことも簡単に行える機能があります。

$ du -b test.txt
101     test.txt
$


この出力結果の数字の部分だけ取り出したいなら以下のようにします。

$ du -b test.txt | awk '{ print $1 }'  
101
$


という具合に出力結果の数字部分だけを取り出すのがいとも簡単に行えます。あまり意味はありませんが、以下のようなことも可能です。

$ du -b test.txt | awk '{ print $2 }'  
test.txt
$


つまり、$1、$2という具合に区切り区切りで文字列を取得できるのです。awkではこの区切りをセパレータと呼び、必要に応じて変更することができます。


JavaScriptやCSSの縮小結果を表示する



次はちょっとした応用例を紹介します。

映画生活やフォト蔵ではJavaScriptファイルをjs_min.pyで、また、CSSを自作のPHPスクリプトで縮小化しています。CSSの方は縮小化だけでなく、縮小前のソースファイルの収集処理なども全部PHPで書いているのですが、JavaScriptの方はjs_min.pyをシェルスクリプトから呼び出す形にしています。単に縮小化するのもなんなので、どれくらい縮小化できたのか調べてみようと思い、
縮小率の計算や出力結果のフォーマットもやりました。以下はscriptaculous.jsのslider.jsとcheck.js、prototype.jsに対して縮小化スクリプトで実行した時の出力です。

$ js_minify.sh slider.js
minified:                               slider.js  10354bytes ->   7631bytes reduction percentage:26.30%
$ js_minify.sh slider.js check.js  prototype.js
minified:                               slider.js  10354bytes ->   7631bytes reduction percentage:26.30%
minified:                                check.js   3364bytes ->   2448bytes reduction percentage:27.23%
minified:                            prototype.js  96653bytes ->  72255bytes reduction percentage:25.24%
$ 


↑のスクリプトではJavaScriptの縮小前と縮小後のソースファイルから縮小率を計算してその結果を表示しています。ファイルを縮小化させるのにjs_min.pyを使っていますが、残りはシェルスクリプト、sedやawk、bcなどの既存のプログラムだけで実現しています。

以下がそのシェルスクリプトです。引数を指定しない場合はJS_SRC_DIR以下の全てのJavaScriptファイルを縮小化します。

実際に使う場合はJS_SRC_DIRとJS_MINIFIED_DIRのディレクトリ構成が同じである必要があります。また、ファイルパスの長さやファイルサイズに合わせてprintfのフォーマットを調整するのがよいでしょう。

#!/bin/sh
HOME_DIR=/home/${USER}/jsdir # 任意のパラメータ
JS_SRC_DIR=${HOME_DIR}/jssrc # 任意のパラメータ
JS_MINIFIED_DIR=${HOME_DIR}/minifiedjs # 任意のパラメータ
JSMIN_DIR=/home/${USER}/bin # 任意のパラメータ
TOTAL_SRC_SIZE=0
TOTAL_MINIFIED_SIZE=0
js_minimize () {
    SRC_PATH=`echo ${JS_SRC_DIR}/$1 | sed -e 's/\.\///g'`
    MINIFIED_PATH=`echo ${JS_MINIFIED_DIR}/$1 | sed -e 's/\.\///g'`
    SRC_SIZE=`du -b ${JS_SRC_DIR}/$1 | awk '{ print $1;}'`
    python ${JSMIN_DIR}/jsmin.py < ${SRC_PATH} > ${MINIFIED_PATH}
    MINIFIED_SIZE=`du -b ${JS_MINIFIED_DIR}/$1 | awk '{ print $1;}'`
    REDUCTION_PERCENTAGE=`echo "(1.0 - ${MINIFIED_SIZE} / ${SRC_SIZE}) * 100" | bc -l`
    REDUCTION_PERCENTAGE=`echo ${REDUCTION_PERCENTAGE} | sed -e 's/^\(-\?\)\./\10./g'`
    echo $1 ${SRC_SIZE} ${MINIFIED_SIZE} ${REDUCTION_PERCENTAGE} | sed -e 's/\.\///g' | awk '{
         printf("minified:%s %dbytes -> %dbytes reduction percentage:%5.2f%%\n", $1, $2, $3, $4);
	 }'
    TOTAL_SRC_SIZE=`expr ${TOTAL_SRC_SIZE} + ${SRC_SIZE}`
    TOTAL_MINIFIED_SIZE=`expr ${TOTAL_MINIFIED_SIZE} + ${MINIFIED_SIZE}`
}
cd ${JS_SRC_DIR}
if [ "$1" != "" ]
then
    while [ "$1" != "" ];do
	if [ -e ${JS_SRC_DIR}/$1 ]
	then
	    js_minimize $1
	else
	    echo ${JS_SRC_DIR}/$1" does not exist."
	fi
	shift 1
    done
else
    for srcname in `find . | egrep '.+\.js$'`;do
	case `basename $srcname` in
            exclude1.js | exclude2.js) ;; # exclude
	    *)
		js_minimize $srcname
	esac
    done
    TOTAL_REDUCTION_PERCENTAGE=`echo "(1.0 - ${TOTAL_MINIFIED_SIZE} / ${TOTAL_SRC_SIZE}) * 100" | bc -l`
    echo ${TOTAL_SRC_SIZE} ${TOTAL_MINIFIED_SIZE} ${TOTAL_REDUCTION_PERCENTAGE} \
	| awk '{ printf("\ntotal     : %dbytes -> %dbytes reduction percentage:%5.2f%%\n", $1, $2, $3); }'
    echo | awk '{ printf("\nAll JavaScript files minified.\n\n"); }'
fi


参考までに



縮小化スクリプトによってフォト蔵ではCSSを約15%、JavaScriptを約30%縮小化することができました。中にはファイルサイズが半分以下になったものもいくつかあります。

縮小化自体は既存のスクリプトを使えば簡単にできるので、少しでもサイトのパフォーマンスを上げたいと思っているならば、js_min.pyやYUI Compressorの導入を検討してみるといいかもしれません。

参考文献



シェルスクリプト基本リファレンス
山森 丈範
技術評論社
売り上げランキング: 29058
おすすめ度の平均: 5.0
5 シェルスクリプト本が数ある中で、
5 Linux、Unixユーザならば、一度、読んでおきたい本



sed&awkプログラミング
sed&awkプログラミング
posted with amazlet at 08.07.17
デール ドゥラティ アーノルド ロビンス
オライリー・ジャパン
売り上げランキング: 86344
おすすめ度の平均: 4.5
5 0から書いたことがありません。
5 正規表現をマスターしましょう
4 Sed&awkプログラミング