24
DBエンジニアに必要だった PYTHONのスキル 山田 @DENZOWILL #STAPY 20160412

DBエンジニアに必要だったPythonのスキル

Embed Size (px)

Citation preview

DBエンジニアに必要だったPYTHONのスキル

山田 聡@DENZOWILL

#STAPY 20160412

WHO AM I?

非プログラマDBエンジニア6年生(PostgreSQL,Oracle)Python歴3年?worked at 株式会社アシストstapy参加7回目

#STAPYでLTを6回やらせていただきました

http://www.slideshare.net/satoshiyamada71697

stapy05-stapy11Pythonでハガキに宛名書きする方法PythonでIPMessenger送る方法15分で情シスに怒られる方法etc

今日のくだらない話はDBエンジニアがPythonで色々やろうと

思った時にぶち当たったつらみ

あるDBエンジニアの現実

普段触る環境LinuxWindowsUnix(AIX, HP-UX, Solaris)

普段触る環境Linux <--ここで楽をしたい

LINUXならPYTHONよりシェルスクリプトじゃない?

実際シェルスクリプトが多いシェルスクリプトで凝ったことするの辛い…Python書き慣れてるし…こっちがいいLinuxならPython最初から入ってるし(新しいリリースとはいっていない)Windowsに移植できる(やったとはいっていない)

POSIX原理主義者の方はそのマサカリを下ろして下

さい

つくらないといけないものシェルスクリプトの代替OSコマンドを叩いて結果を受けとる感じの処理が多いファイルの読み書きも多い客先では追加インストールができないケースも多いので標準モジュール縛りプレイ

よく言われることls でファイルのリストがほしいOSコマンドを叩きたいある文字列ふくまれてるかチェックしたい時刻処理全般XMLとかHTMLとか解釈したいsudoしたい

LS でファイルのリストがほしいls -lR *.py的なことがしたい

os.walkで全部拾ってglobでマッチング

def ls(start_dir, match_rule="*"):    """    指定した条件を満たすファイル一覧を取得する    :param start_dir:捜索開始位置    :param match_rule: globに渡すファイル名のマッチングルール    :return: dir_list, file_list    """    file_list = []    for dirpath, dirnames, filenames in os.walk(start_dir):        for filename in filenames:            tmp_file_name = os.path.join(dirpath, filename)            # ファイル名がルールを満たすか            if glob.fnmatch.fnmatch(filename, match_rule):                file_list.append(tmp_file_name)

    return file_list

OSコマンドを叩きたいsubProcessで出来るcommunicate()で、がっと実行して結果を取る

def exec_os_command(command_str):    """    OSコマンドを実行する。コマンドはshell上で実行される

    :param command_str:実行するコマンド。オプションなども文字列で渡す    :return:    """    # 現在の環境変数をコピーしておく    child_env = os.environ.copy()    # LANG=Cが安定    child_env["LANG"] = "C"    p = subprocess.Popen(        command_str,stdin=subprocess.PIPE,stdout=subprocess.PIPE,        stderr=subprocess.PIPE,env=child_env,        shell=True    )    # EOF を送りつける    stdout, stderr = p.communicate()    return stdout, stderr

ある文字列ふくまれてるかチェックしたい

re(り?れ?)でOK

In [7]: stdout, stderr = exec_os_command("ls ­l /")In [9]: bin_pattern = re.compile(r"bin")In [10]: for line in stdout.split("\n"):   ....:     if bin_pattern.search(line):   ....:         print line   ....:         drwxr­xr­x   2 root root   4096 Mar  8 09:51 bindrwxr­xr­x   2 root root  12288 Mar 29 08:35 sbin

時刻処理全般datetimeただし、2.4ではstrptime/strftimeがないので注意

def to_date(date_string, date_format):    """    日付文字列をdatetime.datetimeオブジェクトにして戻す    :param date_string: 処理対象の文字列    :param date_format: 日付フォーマット    :return:    """    # 2.4でstrptimeがないためのハック    if hasattr(datetime, 'strptime'):        # python 2.6­        strptime = datetime.strptime    else:        # python 2.4 equivalent        strptime = lambda date_string, format: datetime.datetime(*( \                                                time.strptime(date_string, format)[

    return strptime(date_string, date_format)

In [15]: to_date("2016­01­02 13:45", "%Y­%m­%d %H:%M")Out[15]: datetime.datetime(2016, 1, 2, 13, 45)

XMLとかHTMLとか解釈したいxmlモジュールつらいBeautfulSoup使いたいBeautifulSoup4は色々依存してる(lxml,html5lib)BeautifulSoup3なら単一ファイル置くだけ!(多分BadKnowHow...)

In [1]: from BeautifulSoup import BeautifulSoupIn [2]: import urllib2In [3]: response = urllib2.urlopen('https://github.com/denzow/ipymessenger')In [4]: html = response.read()In [5]: soup = BeautifulSoup(html)In [7]: soup.title.stringOut[7]: u'GitHub ­ denzow/ipymessenger: IP messenger via python library'In [16]: link_list = soup.findAll("a")In [17]: link_list[1].get("href")Out[17]: u'https://github.com/'

SUDOしたいroot実行のスクリプトから特定ユーザにsudoしてコマンド叩きたいuid,gidを特定してos.setuid/os.setgidすればOK!def check_uid_and_gid(user_name):    """    ユーザのuidとgidを取得する    :param user_name: ユーザ名    :return: uid, gid    """    # passwdを開いて確認スル    etc_passwd_file = open("/etc/passwd")    etc_passwd = etc_passwd_file.readlines()    etc_passwd_file.close()    attrs = [ None for x in range(4)]    for line in etc_passwd:        if line.find(user_name+":") == 0:            # v1124:x:501:502::/home/v1124:/bin/bash            attrs = line.split(":")            break    return int(attrs[2]), int(attrs[3])

class SudoSetUp(object):    """    Subprocessに渡してsudoさせるためのクラス    """    def __init__(self, uid, gid):        self.uid = uid        self.gid = gid

    def sudo(self):        # 指定されたIDですでに起動しているなら何もしない        if self.uid == os.getuid() and self.gid == os.getgid():            pass        else:            os.setgid(self.gid)            os.setuid(self.uid)

あとはsubprocess.Popenのpreexec_fnに引き渡すだけで、fork時にuid/gidを書き換えて実行してくれる

def exec_os_command(command_str, user_name, return_code_check=True):    """    OSコマンドを実行する。コマンドはshell上で実行される:    :return:    """    # 現在の環境変数をコピーしておく    child_env = os.environ.copy()    # LANG=Cが安定    child_env["LANG"] = "C"    # passwdからuid/gidの取得    uid, gid = check_uid_and_gid(user_name)    sudoset = SudoSetUp(uid, gid)

    p = subprocess.Popen(        command_str,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE,        preexec_fn=sudoset.sudo, # ここ!        env=child_env,        shell=True    )

まとめPythonで泥臭いことをかっこ良くやろうシェルスクリプトの代替でもやってける早くRHELはPython3標準にしてくれ

ご清聴ありがとうございました。引き続きご歓談をお楽しみ下さい。

終わり