end0tknr's kipple - web写経開発

太宰府天満宮の狛犬って、妙にカワイイ

tensorflowによる勾配降下法 ( deep learning & python )

github.com

↑こちらの Chapter1の写経。

前準備 - 使用する関係式

STEP1 : 予測式 - 1~12月の気温を予測

 \displaystyle
y = w_0 x^{0} + w_1 x^{1} + w_2 x^{2} + w_3 x^{3} + w_4 x^{4}
y = w x

 \displaystyle
y = X w

 \displaystyle
y = \left(
    \begin{array}{c}
      y_1 \\
      y_2 \\
      \vdots \\
      y_{12}
    \end{array}
  \right)

 \displaystyle
X = \left(
    \begin{array}{cccc}
      1^{0}  &   1^{1} &  \ldots &  1^{4} \\\
      2^{0}  &   2^{1} &  \ldots &  2^{4} \\\
      \vdots &  \vdots &  \ddots & \vdots \\\
      12^{0} &  12^{1} &  \ldots & 12^{4}
    \end{array}
  \right)

 \displaystyle
w = \left(
    \begin{array}{c}
      w_0 \\
      w_1 \\
      \vdots \\
      w_{4}
    \end{array}
  \right)

STEP2 : 誤差関数

 \displaystyle
E = \frac{1}{2}\sum_{n=1}^{12} (y_n - t_n)

最小二乗法や、ニュートン・ラフソン法を思い出します。

で、実装

#!/usr/local/bin/python
# -*- coding: utf-8 -*-
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

def main():
    #予測関数
    x = tf.placeholder(tf.float32, [None, 5])
    w = tf.Variable(tf.zeros([5, 1]))
    y = tf.matmul(x, w)
    #実測値が入る行列
    t = tf.placeholder(tf.float32, [None, 1])
    #誤差関数
    loss = tf.reduce_sum(tf.square(y-t))
    #勾配降下法によるトレーニングアルゴリズム
    train_step = tf.train.AdamOptimizer().minimize(loss)

    sess = tf.Session()
    sess.run(tf.initialize_all_variables())

    #トレーニングデータ
    train_t = np.array([5.2, 5.7, 8.6, 14.9, 18.2, 20.4,
                        25.5, 26.4, 22.8, 17.5, 11.1, 6.6])
    train_t = train_t.reshape([12,1])
    train_x = np.zeros([12, 5])
    for row, month in enumerate(range(1, 13)):
       for col, n in enumerate(range(0, 5)):
            train_x[row][col] = month**n

    #勾配降下法によるの最適化の繰り返し
    i = 0
    for _ in range(1000000):
        i += 1
        sess.run(train_step, feed_dict={x:train_x, t:train_t})
        if i % 10000 == 0:
            loss_val = sess.run(loss, feed_dict={x:train_x, t:train_t})
            print ('Step: %d, Loss: %f' % (i, loss_val))

    #トレーニング後のパラメーターの値を確認
    w_val = sess.run(w)
    print w_val

#トレーニング後のパラメーターを用いて、予測気温を計算する関数を定義
def predict(x):
    result = 0.0
    for n in range(0, 5):
        result += w_val[n][0] * x**n
    return result


if __name__ == '__main__':
    main()

↑こう書くと↓こう表示されます

[endo@cent7 TENSORFLOW]$ ./foo.py 
Step: 10000, Loss: 31.012341
Step: 20000, Loss: 29.450821
  :
Step: 970000, Loss: 12.155926
Step: 980000, Loss: 34.782570
Step: 990000, Loss: 12.154196
Step: 1000000, Loss: 12.153559
[[ 10.88772202]
 [ -9.05010319]
 [  3.99193835]
 [ -0.44603682]
 [  0.01444708]]

python 2.7 に _tkinter moduleをinstall

python 2.7で “import matplotlib.pyplot as plt” したら、 tkinter がなく errorとなった為。 tkinter の依存ライブラリ/モジュールはきちんと理解していませんが、次のように作業すると、解消。

# yum install tkinter
# yum install tk tcl tk-devel

$ wget https://www.python.org/ftp/python/2.7.13/Python-2.7.13.tgz
$ tar -zxvf Python-2.7.13.tgz
$ cd Python-2.7.13
$ ./configure --enable-optimizations
$ make
$ make test
$ su -
# make install

参考url

http://tkinter.unpythonic.net/wiki/How_to_install_Tkinter

PMD で java の循環的複雑度(code metrics CyclomaticComplexity )を計測

https://pmd.github.io/ https://pmd.github.io/pmd-5.5.4/pmd-java/ https://pmd.github.io/pmd-5.5.4/usage/running.html

install

eclipse plug-inもあると思いますが、今回は、command-line用をinstall.

$ cd /home/endo/local
$ wget https://downloads.sourceforge.net/project/pmd/pmd/5.5.4/pmd-bin-5.5.4.zip
$ unzip pmd-bin-5.5.4.zip

run pmd

https://pmd.github.io/pmd-5.5.4/usage/running.html ↑ここにも記載がありますが、↓こんな感じで、実行&表示

$ ~/local/pmd-bin-5.5.4/bin/run.sh pmd \
     -dir /home/endo/tmp/src \
     -format text \
     -rulesets java-basic,java-codesize 
/home/endo/tmp/src/HttpComm.java:39:    This class has too many methods, consider refactoring it.
/home/endo/tmp/src/JsonUtil.java:1: This class has a bunch of public methods and attributes
/home/endo/tmp/src/JsonUtil.java:19:    Avoid really long classes.
/home/endo/tmp/src/JsonUtil.java:19:    The class 'JsonUtil' has a Cyclomatic Complexity of 3 (Highest = 13).
/home/endo/tmp/src/JsonUtil.java:19:    The class 'JsonUtil' has a Modified Cyclomatic Complexity of 3 (Highest = 13).
/home/endo/tmp/src/JsonUtil.java:19:    The class 'JsonUtil' has a Standard Cyclomatic Complexity of 3 (Highest = 13).
/home/endo/tmp/src/JsonUtil.java:23:    This class has too many methods, consider refactoring it.
/home/endo/tmp/src/JsonUtil.java:221:   The method 'getNode' has a Cyclomatic Complexity of 11.
/home/endo/tmp/src/JsonUtil.java:221:   The method 'getNode' has a Modified Cyclomatic Complexity of 11.
/home/endo/tmp/src/JsonUtil.java:221:   The method 'getNode' has a Standard Cyclomatic Complexity of 11.
    :                                        :
/home/endo/tmp/src/TimeUtil.java:583:   These nested if statements could be combined

java-basic,java-codesize 以外のrulesetは、pmd付属のjarの内容をご覧下さい

perljavascriptのmetricsは、以前のエントリ参照

end0tknr.hateblo.jp

snakeyaml for java による yaml load/read

javaにおけるyaml用ライブラリはいくつもあるようですが、何となく今日はsnakeYAML.

package jp.end0tknr;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.Map;

import org.yaml.snakeyaml.Yaml;

public class TestSnakeYaml {
    public TestSnakeYaml() {}

    public static void main(String[] args) {
        String confFilePath = "resource/test.yaml";
        String encoding = "UTF-8";
        
        File file = new File(confFilePath);
        FileInputStream input;
        InputStreamReader stream;
        try {
            input = new FileInputStream(file);
            stream = new InputStreamReader(input,encoding);
        } catch (FileNotFoundException | UnsupportedEncodingException e) {
            System.out.println(e.getClass().getName()+ 
                    " fail open file "+ confFilePath);
            return;
        }        
        
        Yaml yaml = new Yaml();
        Map yamlMap = (Map<String, ?>) yaml.load(stream);

        for(Object atriKeyTmp : yamlMap.keySet() ){
            String atriKey = (String) atriKeyTmp;
            System.out.println( yamlMap.get(atriKey).toString() );
        }
    }
}
common:
  system_name: ほげほげ
  encode: utf8
  #yes/no
#  debug_mode: yes
db:
  host: localhost
  port: 3306
  db_name: testdb
  db_user: root
  db_pass: 
  db_opt:
    AutoCommit: 0
    mysql_enable_utf8: 1
  client_encoding: utf8

↑こう書くと、↓こう表示されます

{system_name=ほげほげ, encode=utf8}
{host=localhost, port=3306, db_name=testdb, db_user=root, db_pass=null, db_opt={AutoCommit=0, mysql_enable_utf8=1}, client_encoding=utf8}

利用したjar

  • snakeyaml-1.18.jar

apache commons configuration for java で INI file を load / read

http://commons.apache.org/proper/commons-configuration/

ini形式の設定ファイルをloadする必要があったので、探したら、見かけた。

※ini以外にも、 .xmlや .properties 等に対応しているようです。 ( 一方で、.json や、.yaml には対応していません )

package jp.end0tknr;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.Iterator;
import org.apache.commons.configuration2.Configuration;
import org.apache.commons.configuration2.INIConfiguration;
import org.apache.commons.configuration2.ex.ConfigurationException;

public class TestApacheCommonsConfiguration {

    public TestApacheCommonsConfiguration() {}

    public static void main(String[] args) {
        String confFilePath = "resource/test.ini";
        String encoding = "SJIS";
        
        File file = new File(confFilePath);
        FileInputStream input;
        InputStreamReader stream;
        try {
            input = new FileInputStream(file);
            stream = new InputStreamReader(input,encoding);
        } catch (FileNotFoundException | UnsupportedEncodingException e) {
            System.out.println(e.getClass().getName()+ 
                    " fail open file "+ confFilePath);
            return;
        }
        
        INIConfiguration configTmp = new INIConfiguration();
        try {
            configTmp.read( new BufferedReader(stream) );
        } catch (ConfigurationException | IOException e) {
            System.out.println(e.getClass().getName()+" fail read file ");
            return;
        }
        
        Configuration config = configTmp;
        Iterator<String> atriKeys = config.getKeys();
        while(atriKeys.hasNext()) {
            String atriKey = (String)atriKeys.next();
            
            if(! config.containsKey(atriKey) ){
                System.out.println( "not exist key "+ atriKey);
            }
            System.out.println( atriKey+ "="+ config.getString(atriKey) );
        }
    }
}
; Test ini file to be included by a configuration definition
[common]
sysTitle = これは、テスト用のタイトルです
[testini]
loaded=yes

↑こう書くと、↓こう表示されます

common.sysTitle=これは、テスト用のタイトルです
testini.loaded=yes

その他 - 参照したjar

  • commons-configuration2-2.1.1.jar
  • commons-logging-1.2.jar
  • commons-beanutils-1.9.2.jar
  • commons-lang3-3.5.jar

pythonで socket + udp通信し、echonet機器一覧を探索

以下、何となく書いて、何となく、動いた程度。 何となくpythonで書いてみたけど、javaでも書くかな? (気が向いたら)

#!/usr/local/bin/python  ## for python 2.7
# -*- coding: utf-8 -*-

import asyncore
import codecs
import netifaces
import socket
import threading
from time import sleep

UDP_RESES = []
UDP_PORT = 3610 # for UDP

def main():
    # 自身のipアドレスを探索
    local_ip = UdpSender.find_local_ip_addr()
    if local_ip == None:
        return None
    
    # echonetのコマンドをudp送信
    sender = UdpSender(local_ip)

    # echonetのコマンド受信用thread
    recv = UdpReceiverThread()
    recv.start()

    echonet_cmd = echonet_cmd_ls() ##echonet機器の一覧取得用コマンド生成

    # 224.0.23.0 とは、ECHONET専用のマルチキャストアドレス
    sender.send_msg("224.0.23.0", echonet_cmd)

    # 複数機器からレスポンスがある場合があるので、少々、待つ
    sleep(3)
    recv.stop()

    # UDP_RESES に各機器からのレスポンスが、echonet電文とIPで入ってます
    for udp_res in UDP_RESES:
        echonet_res = parse_echonet_res(udp_res[0])
        print(udp_res)
        print(echonet_res)

    # 後はお好きに...



def parse_echonet_res(echonet_res):
    res_cols = [echonet_res[ 0: 4],  ## echonetであることの宣言
                echonet_res[ 4: 8],  ## 自由欄
                echonet_res[ 8:14],  ## SEOJ(送信元機器) 0ef001=ノード
                echonet_res[14:20],  ## DEOJ(送信先機器) 05ff01=コントローラ
                echonet_res[20:22],  ## 応答code. 71=set 72=get
                echonet_res[22:24],  ## 処理プロパティ数
                echonet_res[24:26],  ## プロパティ名 d6=自ノードlist.
                echonet_res[26:28],  ## PDC. 後のbyte数
                echonet_res[28:36]]  ## 0105ff01 = 1個(01)x05ff01
    return res_cols


## http://qiita.com/miyazawa_shi/items/725bc5eb6590be72970d
def echonet_cmd_ls():
 
    cmd_cols = ["1081",   ## echonetであることの宣言
                "0000",   ## 自由欄
                "05ff01", ## SEOJ(送信元機器) 05ff01=コントローラ
                "0ef001", ## DEOJ(送信先機器) 0ef001=ノード
                "62",     ## 60=set, 61=set(要:応答), 62=get
                "01",     ## 処理プロパティ数
                "d6",     ## プロパティ名 d6=自ノードlist.
                          ## https://echonet.jp/spec_v112_lite/ にある
                          ## 第2部 ECHONET Lite 通信ミドルウェア仕様の
                          ## 6.11.1 ノードプロファイル詳細規定 参照
               "00"]
    echonet_cmd = "".join(cmd_cols)
    return echonet_cmd



class UdpSender():
    def __init__(self, local_ip):
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.sock.setsockopt(socket.IPPROTO_IP,
                             socket.IP_MULTICAST_IF,
                             socket.inet_aton(local_ip))
        
    def send_msg(self, ip,message):
        decode_hex = codecs.getdecoder("hex_codec")
        msg = decode_hex( message )[0]
        self.sock.sendto(msg, (ip, UDP_PORT))

    def close(self):
        self.sock.close()

    @staticmethod
    def find_local_ip_addr(find_iface_name=None):
        for iface_name in netifaces.interfaces():
            iface_data = netifaces.ifaddresses(iface_name)
            af_inet = iface_data.get(netifaces.AF_INET)
        
            if not af_inet: continue

            ip_addr = af_inet[0]["addr"]
            
            if find_iface_name == None:
                return ip_addr
            elif iface_name == find_iface_name:
                return ip_addr
        return None

class UdpReceiverThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.server = UdpReceiver()

    def run(self):
        asyncore.loop()

    def stop(self):
        self.server.close()
        self.join()

        
class UdpReceiver(asyncore.dispatcher):
    def __init__(self):

        asyncore.dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.bind(("0.0.0.0",UDP_PORT))
        return

    def handle_read(self):
        data = self.recvfrom(4096)
        encode_hex = codecs.getencoder("hex_codec")
        global UDP_RESES
        UDP_RESES.append([encode_hex(str(data[0]))[0], data[1][0]])
        return

if __name__ == '__main__':
    main()

↑こう書くと、↓こう出力されます

C:\home\endo\tmp>\Python27\python.exe foo.py
['108100000ef00105ff017201d6040105ff05', '192.168.0.15']
['1081', '0000', '0ef001', '05ff01', '72', '01', 'd6', '04', '0105ff05']

たまにエラー…windows環境だから?

たまに、次のようなエラーが出力されますので、気が向いたら、調べます。

C:\home\endo\tmp>\Python27\python.exe foo.py
Exception in thread Thread-1:
Traceback (most recent call last):
  File "C:\Python27\lib\threading.py", line 801, in __bootstrap_inner
    self.run()
  File "foo.py", line 106, in run
    asyncore.loop()
  File "C:\Python27\lib\asyncore.py", line 216, in loop
    poll_fun(timeout, map)
  File "C:\Python27\lib\asyncore.py", line 145, in poll
    r, w, e = select.select(r, w, e, timeout)
error: (10038, '\x83\\\x83P\x83b\x83g\x88\xc8\x8aO\x82\xcc\x82\xe0\x82\xcc\x82\xc9\x91\xce\x82\xb5\x82\xc4\x91\x80\x8d\xec\x82\xf0\x8e\xc0\x8ds\x82\xb5\x82\xe6\x82\xa4\x82\xc6\x82\xb5\x82\xdc\x82\xb5\x82\xbd\x81B')

参考にさせて頂いたurl

ソフトウエア取得/改修に伴う 資産計上 or 経費処理 - 7-8-6の2 (ソフトウエアに係る資本的支出と修繕費)

国税庁が公開する 基本通達・法人税法 の 7-8-6の2 (ソフトウエアに係る資本的支出と修繕費)にある通り、 - 「ソフトウエアの機能の追加、向上」⇒(ならば) 資本的支出(≒資産計上)。 - バグ修正等、現状の維持該当 ⇒ 修繕費(≒経費処理OK) らしい。

https://www.nta.go.jp/shiraberu/zeiho-kaishaku/tsutatsu/kihon/hojin/07/07_08.htm

例えば、消費税法改正に伴うソフト修正は、経費処理がOKのよう。 https://www.nta.go.jp/shiraberu/zeiho-kaishaku/joho-zeikaishaku/hojin/0309/01.htm

HTML-lint tool (≠ metrics tool)

html-tidy がいいのかな?

$ wget http://binaries.html-tidy.org/binaries/tidy-5.2.0/tidy-5.2.0-64bit.rpm
$ su
# rpm -ivh tidy-5.2.0-64bit.rpm
$ tidy --help
  • SourceMonitor は、win環境専用
  • Another HTML-lint 5” は、web専用
  • HTML::Lint for perl はratingが低い

ので

pythonでの基数変換の基本は format() ←→ int()、より複雑ならMath::BaseCalc のclone?

pythonの基数変換において、2, 8, 16進数なら…

format() , int()が利用できるので…

#!/usr/local/bin/python
# -*- coding: utf-8 -*-

def main():
    from_int()
    to_int()

def from_int():
    org_int = 10
    for base in ["b","o","x","X"]:
        from_int = format(org_int,base)
        print base +":"+str(org_int) +"->"+ str(from_int)
    print ''

def to_int():
    for org_str, base in {"1010":2, "12":8, "a":16}.items():
        print str(base)+":"+ org_str +"->"+ str( int(org_str, base) )

if __name__ == '__main__':
    main()

↑こう書くと↓こう実行できます

$ ./foo.py
b:10->1010
o:10->12
x:10->a
X:10->A

16:a->10
8:12->10
2:1010->10

2, 8, 16進数以外の基数変換であれば

Math::BaseCalc for perlのcloneを書く必要があるのかな? http://search.cpan.org/perldoc?Math%3A%3ABaseCalc

Excel::Writer::XLSX::Utility for perlで excelのA1形式座標←→R1C1形式座標の変換

Math::BaseCalc かと思ったら、Excel::Writer::XLSX::Utility を使うようです。 http://search.cpan.org/perldoc?Excel%3A%3AWriter%3A%3AXLSX

#!/usr/local/bin/perl
use strict;
use warnings;
use utf8;
# use Math::BaseCalc;
use Excel::Writer::XLSX::Utility;
use Data::Dumper;

main();

sub main {

    my $co_a1_org = "C2";
    my ( $row, $col ) = xl_cell_to_rowcol( $co_a1_org );
    print "$co_a1_org -> ($row, $col)\n";

    my @co_r1c1_org = (2,30);
    my $co_a1 = xl_rowcol_to_cell( @co_r1c1_org );
    print "($co_r1c1_org[0], $co_r1c1_org[1]) -> $co_a1\n";
}

↑こう書くと、↓こう変換できます

$ ./foo.pl 
C2 -> (1, 2)
(2, 30) -> AE3

Email::Stuffer for perlによるメール送信でReturn-Pathをセット

http://end0tknr.hateblo.jp/entry/20150819/1439950536 http://end0tknr.hateblo.jp/entry/20170205/1486293131

Email::Stuffer シリーズ?の第3弾?

メールソフト(MUA)に表示されるアドレスとは、別のアドレスにエラーメールを返送したい為、メモ。 まぁ、ググれば、分かるんですけどね

envelope from と header from の違い?

fromのtype 説明(例)
envelope from 封筒に書かれる差出人. 宛先不明時はこちらへ返送
header from 便箋に書かれる差出人

envelope from と Return-Pathの関係

宛先不明メールは、Return-Path宛に返送されます。 しかし、メールヘッダのReturn-Pathはメールサーバ経由時に書換えられる為、 単純にメールヘッダにReturn-Pathを設定しても意味がありません。

envelope fromこそが、メールサーガ経由時にReturn-Pathに設定される為、 envelope fromにエラーメールの返送先を設定する必要があります。

Email::Stuffer for perlによるメール送信でReturn-Pathをセット

するには、次のように書きます。

ただし、試した範囲では、smtp.gmail.com での送信では、 好みのReturn-Path(envelop from)は設定できませんでした。

#!/usr/local/bin/perl
use strict;
use warnings;
use utf8;
use Email::Stuffer;
use Email::Sender::Transport::SMTP;
use Email::Sender::Transport::SMTP::TLS;
use Data::Dumper;

my $FROM =     'ないしょ@ないしょ.com'; ## HEADER  FROM
my $REPLY_TO = 'ひみつ@ないしょ.com';   ## ENVELOP FROM
my $SUBJECT = 'TEST MAIL';
# auth smtpの場合
my $SMTP_CONF = {host => 'xxx.yyy.zzz.co.jp',
                 port => 587,
                 sasl_username => 'ないしょ',
                 sasl_password => 'ないしょ',
                 debug=>1};
# 素の場合
# my $SMTP_CONF = {host => 'ないしょ.co.jp', port => 25};
# auth smtp(tls)の場合
my $SMTP_TLS_CONF = {host => 'smtp.gmail.com',
                     port => 587,
                     username=>'ないしょ@gmail.com',
                     password=>'ないしょ', # APPのパスワード
                     debug=>1 };


main( @ARGV);

sub main {
    my ($mailto) = @_;
    
    my $email = Email::Stuffer->new();

    my $smtp = get_smtp_obj();
#    my $smtp = get_smtp_obj_tls();
    $email->transport( $smtp );

    $email->subject($SUBJECT);
    $email->from($FROM);            ## HEADER FROM
    $email->to($mailto);
    $email->header('Reply-To' => $REPLY_TO);

    my $txt_mime =  get_txt_mime();  ## 代替text part
    my $html_mime = get_html_mime(); ## html part

    push(@{$email->{parts}}, $txt_mime);
    push(@{$email->{parts}}, $html_mime);

    $email->send_or_die({from=>$REPLY_TO});  ## ENVELOP FROM
}

sub get_smtp_obj_tls {
    my $smtp = Email::Sender::Transport::SMTP::TLS->new($SMTP_TLS_CONF);
    return $smtp;
}

sub get_smtp_obj {
    my $smtp = Email::Sender::Transport::SMTP->new($SMTP_CONF);
    return $smtp;
}

sub get_txt_mime {

    my $attr = {content_type => 'text/plain',
                charset      => 'utf-8',
                encoding     => 'quoted-printable',
                format       => 'flowed'};
    my $body =<<EOF;
これは、テキストメールのテストです。
EOF

    my $mime = Email::MIME->create(attributes => $attr,
                                   body_str   => $body);
    return $mime;
}

sub get_html_mime {
    my $attr = {content_type => 'text/html',
                charset      => 'utf-8',
                encoding     => 'quoted-printable'};
    my $body =<<EOF;
<html>
<head></head>
<body>
これは、<b>HTML</b>メールのテストです。
</body>
</html>
EOF
    my $mime = Email::MIME->create(attributes => $attr,
                                   body_str   => $body);
    return $mime;
}

1;

Email::Stuffer for perl による HTMLメール送信 (HTML+TXTマルチパート)

Email::Stuffer や Email::Sender::Transport::SMTP 、 Email::Sender::Transport::SMTP::TLS に殆どおまかせなので、 以下のように書くだけでOK。

#!/usr/local/bin/perl
use strict;
use warnings;
use utf8;
use Email::Stuffer;
use Email::Sender::Transport::SMTP;
use Email::Sender::Transport::SMTP::TLS;
use Data::Dumper;

my $FROM = 'ないしょ@gmail.com';
my $SUBJECT = 'TEST MAIL';
# auth smtpの場合
my $SMTP_CONF = {host => 'ないしょ.co.jp',
                 port => 587,
                 sasl_username => 'ないしょ',
                 sasl_password => 'ないしょ',
                 debug=>1
                };
# 素の場合
# my $SMTP_CONF = {host => 'ないしょ.co.jp',
#                  port => 25};
# auth smtp(tls)の場合
my $SMTP_TLS_CONF = {host => 'smtp.gmail.com',
                     port => 587,
                     username=>'ないしょ',
                     password=>'ないしょ', # APPのパスワード
                     debug=>1
                    };


main( @ARGV);

sub main {
    my ($mailto) = @_;
    
    my $email = Email::Stuffer->new();

#    my $smtp = get_smtp_obj();
    my $smtp = get_smtp_obj_tls();
    $email->transport( $smtp );

    $email->subject($SUBJECT);
    $email->from($FROM);
    $email->to($mailto);

    my $txt_mime =  get_txt_mime();  ## 代替text part
    my $html_mime = get_html_mime(); ## html part

    push(@{$email->{parts}}, $txt_mime);
    push(@{$email->{parts}}, $html_mime);

    
    $email->send_or_die;
}

sub get_smtp_obj_tls {
    my $smtp = Email::Sender::Transport::SMTP::TLS->new($SMTP_TLS_CONF);
    return $smtp;
}

sub get_smtp_obj {
    my $smtp = Email::Sender::Transport::SMTP->new($SMTP_CONF);
    return $smtp;
}


sub get_txt_mime {

    my $attr = {content_type => 'text/plain',
                charset      => 'utf-8',
                encoding     => 'quoted-printable',
                format       => 'flowed'};
    my $body =<<EOF;
これは、テキストメールのテストです。
EOF

    my $mime = Email::MIME->create(attributes => $attr,
                                   body_str   => $body);
    return $mime;
}

sub get_html_mime {

    my $attr = {content_type => 'text/html',
                charset      => 'utf-8',
                encoding     => 'quoted-printable'};
    my $body =<<EOF;
<html>
<head></head>
<body>
これは、<b>HTML</b>メールのテストです。
</body>
</html>
EOF

    my $mime = Email::MIME->create(attributes => $attr,
                                   body_str   => $body);
    return $mime;
}

1;

perlのCLIでコマンドライン引数を受取りは、Getopt::Long::GetOptions()

↓こんな感じで使用します。とうより、自分用メモ。

#!/usr/local/bin/perl
use strict;
use warnings;
# http://tagomoris.hatenablog.com/entry/20120918/1347991165
use Getopt::Long qw(:config posix_default no_ignore_case gnu_compat);
use Data::Dumper;

my $cmd_opts = {};

main();

sub main {

    my $cmd_opts = {}; # Getopt::Long::GetOptions により引き数が蓄積
    
    my @defined_opts =
        ('conf_file=s','mail_type=s','mail_data=s','subject=s','output=s');
    #不明なoptionが与えられた場合
    unless(Getopt::Long::GetOptions($cmd_opts,@defined_opts)){
        print_usage();
        return;
    }

    #本来?は、ここで、与えられた引き数をvalidataion
    if(scalar(keys %$cmd_opts)==0){
        print_usage();
        return;
    }

    
    #で、その後の処理に入る...
}

sub print_usage {

    print "Usage: $0 COMMAND [OPTION]\n";
    print '  --conf_file=$FILE_PATH',"\n";
    print "  --mail_data=[NULL, BULK_MAIL]\n";
    print '  --subject=$SUBJECT',"\n";
    print '  --output=[NULL, FILE, $MAILTO]',"\n";

}

sleep() , select() , Time::HiRes::sleep() による perlのsleep処理

perlのsleepでは、1秒単位のsleepは組込み関数のsleep()、 1秒未満単位のsleepは Time::HiResの sleep() を使用しますが、 select(undef, undef, undef, $sleep_time)でも、Time::HiRes::sleep() と同様の動作をできるらしい。

#!/usr/local/bin/perl
use strict;
use warnings;
use Time::HiRes;


main();

sub main {

    for my $interval (1.5 , 1.0, 0.5){
        my $start_time = Time::HiRes::time();
        # http://perldoc.perl.org/functions/sleep.html
        sleep($interval);
        my $end_time = Time::HiRes::time();
        my $slept_time = $end_time - $start_time;

        print "sleep( $interval )->slept time $slept_time sec\n";
    }
    print "\n";

    for my $interval (1.5 , 1.0, 0.5){
        ## http://perldoc.perl.org/functions/select.html
        ## http://perldoc.jp/func/select
        my $start_time = Time::HiRes::time();
        select(undef, undef, undef, $interval); ## = sleep

        my $end_time = Time::HiRes::time();
        my $slept_time = $end_time - $start_time;

        print
            "select(undef,undef,undef,$interval)->slept time $slept_time sec\n";
    }
    print "\n";

    for my $interval (1.5 , 1.0, 0.5){
        my $start_time = Time::HiRes::time();
        ## http://search.cpan.org/perldoc?Time%3A%3AHiRes
        Time::HiRes::sleep($interval);
        my $end_time = Time::HiRes::time();
        my $slept_time = $end_time - $start_time;

        print
            "Time::HiRes::sleep($interval)->slept time $slept_time sec\n";
    }
}

つまり↑こう書くと、↓こう表示されます

$ ./foo.pl 
sleep( 1.5 )->slept time 1.00030517578125 sec
sleep( 1 )->slept time 1.00028395652771 sec
sleep( 0.5 )->slept time 9.05990600585938e-06 sec

select(undef,undef,undef,1.5)->slept time 1.50168013572693 sec
select(undef,undef,undef,1)->slept time 1.00239300727844 sec
select(undef,undef,undef,0.5)->slept time 0.500741958618164 sec

Time::HiRes::sleep(1.5)->slept time 1.50391101837158 sec
Time::HiRes::sleep(1)->slept time 1.00040507316589 sec
Time::HiRes::sleep(0.5)->slept time 0.500246047973633 sec

が、perl best practice (PBP)では、お作法違反

らしく、↓このように怒られます

$ perlcritic foo.pl 
"select" used to emulate "sleep" at line 26, column 9.  See page 168 of PBP.  (Severity: 5)