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-stapy11 Pythonでハガキに宛名書きする方法 PythonでIPMessenger送る方法 15分で情シスに怒られる方法 etc
今日のくだらない話は DBエンジニアがPythonで色々やろうと 思った時にぶち当たったつらみ
あるDBエンジニアの現実
普段触る環境 Linux Windows Unix(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 bin drwxr­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 BeautifulSoup In [2]: import urllib2 In [3]: response = urllib2.urlopen('https://github.com/denzow/ipymessenger') In [4]: html = response.read() In [5]: soup = BeautifulSoup(html) In [7]: soup.title.string Out[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=subproces         preexec_fn=sudoset.sudo, # ここ!         env=child_env,         shell=True     )
まとめ Pythonで泥臭いことをかっこ良くやろう シェルスクリプトの代替でもやってける 早くRHELはPython3標準にしてくれ
ご清聴ありがとうございました。 引き続きご歓談をお楽しみ下さい。
終わり

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