Hatena::ブログ(Diary)

# rm -rf / Twitter

2014-04-22

アラビア数字を漢数字に変換

ひょんなことからペチパーを目指すことになったので、ちょこっとお勉強。

ネタは以前のものから拝借。

<?php
    function convert($number) {
        $array1 = array('', '', '', '', '', '');
        $array2 = array('', '', '', '', '', '', '', '', '', '');
        $chou = (int)($number / 1000000000000);
        $rest = $number % 1000000000000;
        $oku = (int)($rest / 100000000);
        $rest = $rest % 100000000;
        $man = (int)($rest / 10000);
        $rest = $rest % 10000;
        $sen = (int)($rest / 1000);
        $rest = $rest % 1000;
        $hyaku = (int)($rest / 100);
        $rest = $rest % 100;
        $juu = (int)($rest / 10);
        $ichi = $rest % 10;
        if(0 < $chou && $chou < 10) {
            $result = $result.$array2[$chou].$array1[0];
        } elseif($chou != 0) {
            $result = $result.convert($chou).$array1[0];
        }
        if(0 < $oku && $oku < 10) {
            $result = $result.$array2[$oku].$array1[1];
        } elseif($oku != 0) {
            $result = $result.convert($oku).$array1[1];
        }
        if(0 < $man && $man < 10) {
            $result = $result.$array2[$man].$array1[2];
        } elseif($man != 0) {
            $result = $result.convert($man).$array1[2];
        }
        if(0 < $sen && $sen < 10) {
            $result = $result.$array2[$sen].$array1[3];
        }
        if(1 < $hyaku && $hyaku < 10) {
            $result = $result.$array2[$hyaku].$array1[4];
        } elseif($hyaku == 1) {
            $result = $result.$array1[4];
        }
        if(1 < $juu && $juu < 10) {
            $result = $result.$array2[$juu].$array1[5];
        } elseif($juu == 1) {
            $result = $result.$array1[5];
        }
        if(0 < $ichi && $ichi < 10) {
            $result = $result.$array2[$ichi];
        }
        return $result;
    }
    echo convert(1234567890123456)."\n";
    echo convert(123456789012)."\n";
    echo convert(12345678)."\n";
    echo convert(1234)."\n";
    echo convert(123)."\n";
    echo convert(12)."\n";
?>

2013-12-28

AWKでRDBを使う

以前に同じようなタイトルの記事を書いた時SQL文の実行自体はPythonスクリプトで行っていましたが、今回はSQLite3のコマンドラインツールでやってみようと思います。

sqlite3コマンドに以下のようなオプションを指定することで取得結果をファイルの入力のように扱うことができます。

  • noheader ヘッダ行を出力しない
  • separator フィールドセパレータを指定
#!/usr/bin/awk -f
BEGIN {
  db = "test.db"
  command = "sqlite3 -noheader -separator \" \" " db " \" %s \""
  create_table = "create table employee (id, name, age)"
  insert1 = "insert into employee values (1, 'taro', 20)"
  insert2 = "insert into employee values (2, 'hanako', 18)"
  insert3 = "insert into employee values (3, 'ichiro', 26)"
  select = "select * from employee order by age"
  drop_table = "drop table employee"
  system(sprintf(command, create_table))
  system(sprintf(command, insert1))
  system(sprintf(command, insert2))
  system(sprintf(command, insert3))
  while((sprintf(command, select) | getline) > 0) {
    print $2, $3
  }
  system(sprintf(command, drop_table))
}

各種RDBMSにはそれぞれコマンドラインツールが付属していますので、応用範囲は広いと思います。

2013-12-26

Pythonのdoctestライブラリ

Pythonにはdoctestというライブラリがあり、メソッドのコメントにテストコードを書いて評価することができます。使い方は簡単で、コメント内の">>> "に続いて式を書いて、その下に戻り値を書くだけです。引数に順序が保証されないセット型や辞書型なども以下の例のようにすることでテストすることができます。これが標準ライブラリに入ってるなんて素晴らしいですね。

def three_times(num):
  """ 
  >>> three_times(-1)
  -3
  >>> three_times(0)
  0
  >>> three_times(1)
  3
  >>> three_times(3)
  9
  """
  return 3 * num 

def three_times_all(list):
  """ 
  >>> three_times_all([-1, 0, 1, 3])
  [-3, 0, 3, 9]
  >>> three_times_all([])
  []
  >>> sorted(three_times_all(set([-1, 0, 1, 3]))) == [-3, 0, 3, 9]
  True
  """
  return [three_times(num) for num in list]

def uppercase(dct):
  """ 
  >>> uppercase({'first':'Micheal', 'last':'Jackson'}) == {'last':'JACKSON', 'first':'MICHEAL'}
  True
  """
  for key, value in dct.items():
    dct[key] = value.upper()
  return dct 

if __name__ == '__main__':
  from doctest import testmod
  testmod()

2013-12-24

Antでバッチジョブを実行する

cronはLinux標準のジョブスケジューラで定刻になるとコマンドやスクリプトを実行してくれる便利な機能ですが、各スケジュールの前後関係が定義できなかったりと少々不便な面があります。Apache Antは有名なビルドツールでJavaの開発にはなくてはならないものですが、今回はこれをバッチジョブの実行に応用してみようと思います。

今回はサンプルとしてジョブを3つ用意しました。それぞれは文字列を出力した後に第1引数を終了コードにセットして終了する、というシンプルなものです。B0001.shはマルチスレッドで並列実行するため、第2引数にそれぞれ異なる値を設定して実行順序が毎回変わることを確認できるようにしています。

#!/bin/sh
#A0001.sh
echo "A0001"
exit $1
#!/bin/sh
#B0001.sh
echo "B0001"
echo $2
exit $1
#!/bin/sh
#C0001.sh
echo "C0001"
exit $1

J0001.xmlには各ジョブの前後関係を定義しています。

A0001.shを単独実行→B0001.shを並列実行→C0001.shを単独実行

といった構成で、後続のジョブは先行ジョブの終了コードを取得して、正常終了している場合に実行するようにしました。

<?xml version="1.0" ?>
<project default="main" basedir="/path/to">
  <target name="main" depends="begin, A0001, B0001, C0001, end">
  </target>

  <target name="begin">
    <echo>
      Begin execute jobnet.
    </echo>
  </target>

  <target name="A0001">
    <exec executable="${basedir}/A0001.sh"
        failonerror="false"
        resultproperty="a0001">
      <arg value="0"/>
    </exec>
    <condition property="a0001_res"> 
      <equals arg1="0" arg2="${a0001}"/>
    </condition>
  </target>

  <target name="B0001" if="a0001_res">
    <parallel>
      <exec executable="${basedir}/B0001.sh"
        failonerror="false"
        resultproperty="b0001_0">
        <arg value="0"/>
        <arg value="0"/>
      </exec>
      <exec executable="${basedir}/B0001.sh"
        failonerror="false"
        resultproperty="b0001_1">
        <arg value="0"/>
        <arg value="1"/>
      </exec>
      <exec executable="${basedir}/B0001.sh"
        failonerror="false"
        resultproperty="b0001_2">
        <arg value="0"/>
        <arg value="2"/>
      </exec>
    </parallel>
    <condition property="b0001_res">
      <and>
        <equals arg1="0" arg2="${b0001_0}"/>
        <equals arg1="0" arg2="${b0001_1}"/>
        <equals arg1="0" arg2="${b0001_2}"/>
      </and>
    </condition>
  </target>

  <target name="C0001" if="b0001_res">
    <exec executable="${basedir}/C0001.sh"
        failonerror="false"
        resultproperty="c0001">
      <arg value="0"/>
    </exec>
    <condition property="c0001_res">
      <equals arg1="0" arg2="${c0001}"/>
    </condition>
  </target>

  <target name="end">
    <echo>
      End execute jobnet.
    </echo>
  </target>
</project>

antコマンドを実行します。

$ ant -buildfile J0001.xml 
Buildfile: /path/to/J0001.xml

begin:
     [echo] 
     [echo]       Begin execute jobnet.
     [echo]     

A0001:
     [exec] A0001

B0001:
     [exec] B0001
     [exec] 0
     [exec] B0001
     [exec] 2
     [exec] B0001
     [exec] 1

C0001:
     [exec] C0001

end:
     [echo] 
     [echo]       End execute jobnet.
     [echo]     

main:

BUILD SUCCESSFUL
Total time: 1 second
$ 

順番通りに実行されていますね。

今度はB0001.shのひとつを異常終了させてみます。

$ ant -buildfile J0001.xml 
Buildfile: /path/to/J0001.xml

begin:
     [echo] 
     [echo]       Begin execute jobnet.
     [echo]     

A0001:
     [exec] A0001

B0001:
     [exec] B0001
     [exec] 0
     [exec] B0001
     [exec] 1
     [exec] B0001
     [exec] 2
     [exec] Result: 1

C0001:

end:
     [echo] 
     [echo]       End execute jobnet.
     [echo]     

main:

BUILD SUCCESSFUL
Total time: 1 second
$ 

C0001.shが実行されていないのが分かると思います。execタスクのfailonerrorをfalseにすることで、ジョブが異常終了した場合にも処理を継続できるようにしています。あとはこれをcronで実行すれば簡易スケジューラの出来上がりです。

2013-12-08

Pythonでヒアドキュメント

Pythonで複数行にまたがる文字列情報を扱いたいけど、外部ファイル化するほどのものでない場合、シェルで言うヒアドキュメントのようなことができます。

html = ''' 
<html>
  <body>
    Hello world
  </body>
</html>
'''

for i in html.split('\n'):
  if i.find('bo') > 0:
    print(i.strip())

Pythonで簡易ログローテーション

Pythonでログローテーションをするスクリプトを書いてみました。Python3系で動作します。

ファイルサイズを見て指定したサイズより大きければ、現在日時をファイル名の末尾に加えてリネームします。

よほど短時間で大量にログに書き込むことがない限り、cronで1分おきに動かせばおおよそ同じくらいのファイルサイズでローテーションされるでしょう。

#log_rotate.py
from os.path import getsize
from os import rename
from datetime import datetime

srcnames = ['./logfile1', './logfile2']

kb = 1024
mb = kb * 1024

def log_rotate():
  for srcname in srcnames:
    size = getsize(srcname)
    if size > 10 * mb :
      dstname = srcname + "." + datetime.now().strftime('%Y%m%d%H%M%S')
      rename(srcname, dstname)

if __name__ == '__main__':
  log_rotate()