よく使いそうなのだけ別途まとめた
他はhttp://pythonjp.sourceforge.jp/dev/library/csv.htm... 参照
bodyを分割して取得するなら
perlでいうLINE
http://d.hatena.ne.jp/t2y-1979/20090909/1252474203 にちょっとした使い方をみつけた
import sys # _getframeのデフォルト引数は0(depth) print(sys._getframe().f_lineno)ただし、_getframeを使うよりinspectモジュールを使うほうがよさそうな気がする
http://d.hatena.ne.jp/t2y-1979/20090909/1252474203 にちょっとした使い方をみつけた
path。windowsだとこうなっていた
print(sys.path) ['C:\\Documents and Settings\\kurt\\デスクトップ', 'C:\\WINDOWS\\system32\\python30.zip', 'C:\\Program Files\\Python30\\DLLs', 'C:\\Program Files\\Python30\\lib', 'C:\\Program Files\\Python30\\lib\\plat-win', 'C:\\Program Files\\Python30', 'C:\\Program Files\\Python30\\lib\\site-packages']配列で定義されているので、自分で独自にライブラリパスを通したい場合は
sys.path.insert(0, "/path/to/lib")とする。PYTHONPATH 環境変数でもよい
# コンソールからとか export PYTHONPATH="/path/to/lib"sys.pathの先頭に追加されるようだ
sys.stdin.encoding sys.stdout.encoding sys.stderr.encoding sys.getdefaultencoding()入出力時は文字コードが何になってようがこれらに設定されている値で入出力される
例外情報を取得。except節の中で実行する。tupleで例外クラスの型情報、例外クラス、traceback例外クラスが返ってくる。たとえば1 / 0のように必ず0で割ろうとして失敗の場合
# python import sys from traceback import print_tb, print_exc try: res = 1 / 0 except: print(sys.exc_info())とすると
(<class 'ZeroDivisionError'>, ZeroDivisionError('int division or modulo by zero' ,), <traceback object at 0x00C0AD00>)と返ってくる
python自身のバージョン
sys.version # 3.0.1 (r301:69561, Feb 13 2009, 20:04:18) [MSC v.1500 32 bit (Intel)]sys.version_infoでもよい。こっちはtupleで返ってくる
sys.version_info # (3, 0, 1, 'final', 0)
perlのsystemとほぼ同じ。いいところは返り値がそのままコマンドの終了コマンドになっている
# こうするか my $exit_value = system "ls -l" >> 8; # こっちか my $exit_value = $? >> 8;pythonはそんなことしなくてよい
exit_value = os.system("ls -l")
perlでいうexec。実行したプロセスになり、結果は返ってこない
# コマンドは絶対パスで記述しないといけない os.execv("/bin/ls", ["", "-l", "-A", "/path/to/dir"])どうもくせがあるようで第2引数で渡すlist(or tuple)のindex0は必ず空として解釈されてしまうみたいなので
os.execv("/bin/cat", ["/etc/passwd"])などと書くと
catを実行したことになってしまう。なので必ず
os.execv("/bin/cat", ["", "/etc/passwd"])と書くこと
perlと同じ。返り値が0の場合は子プロセスとなる
#!/usr/local/bin/python3.0 import os, sys, time pid = os.fork() if pid == 0: print("child process: %i" % (os.getpid())) time.sleep(10) sys.exit(0) else: print("parent process: %i" % (os.getpid())) childpid, status = os.wait() print("%i %i" % (pid, status >> 8))waitしているので、この場合だと10秒後、親プロセス側で子プロセスのpidと終了コードがwaitによって返される。複数forkする場合はこんな感じか
import os, sys, time children = [] for i in range(1, 4): pid = os.fork() if pid: children.append(pid) elif pid == 0: print("child process[%i] start for %i" % (os.getpid(), i)) time.sleep(i) print("child process[%i] end for %i" % (os.getpid(), i)) sys.exit(0) for child in children: pid, status = os.waitpid(child, 0); print("process[%i] exit_status:%i" % (child, status >> 8));
↑のwaitpidをwait4に置き換えると
for child in children: pid, status, resource = os.wait4(child, 0); print("process[%i] exit_status:%i" % (child, status >> 8)); print(resource)resourceはgetrusage(2)で取得できるstruct rusage型が構造体の状態で返って来る。詳細はhttp://pythonjp.sourceforge.jp/dev/library/resourc... を見ること。perlはちなみにProc::Wait3までしかないけどwait4作れないこともないな
環境変数取得。プロパティ
余計な環境変数を削除とかしたい場合はperlだと
for key in os.environ.keys(): print("%s: %s" % (key, os.environ[key]))特定の環境変数取得だけならgetenvでもよい
print(os.getenv('HOME'))環境変数設定はenvironに新しいkeyを定義してあげればよい
os.environ['FOO'] = 'BAR'
余計な環境変数を削除とかしたい場合はperlだと
$ENV{PATH} = "/usr/bin:/usr/sbin:/usr/local/bin"; $ENV{LANG} = "C"; $ENV{TZ} = "JST-9"; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};みたいなことしてるが同じようなことをする場合は
os.environ['PATH'] = "/usr/bin:/usr/sbin:/usr/local/bin" os.environ['LANG'] = "C" os.environ['TZ'] = "JST-9" # まとめて消す方法ありそうだが for key in filter(lambda x: x in os.environ, ["BASH_ENV", "ENV", "IFS", "CDPATH"]): del(os.environ[key])
perlでいうDirHandle的な。存在しないディレクトリの場合は例外。perlと違って.と..は返ってこない
dir = "/path/to/dir" for entry in os.listdir(dir): print(entry)
mkdirはすでに存在、rmdirは存在していない場合は例外。rmdirはディレクトリ内に何かある場合も消せない
dir = "/path/to/dir" os.mkdir(dir) os.rmdir(dir)
find的な感じ。返り値は[dirpath, dirnames, filenames]となる。dirnames, filenames自体が配列になっている
# ファイルのみを表示することにする for (dirpath, dirnames, filenames) in os.walk(dir): for filename in filenames: print(os.path.join(dirpath, filename))
ディレクトリとファイル(というわけでもないんだが)に分ける。string objectのsplitのような動作と思っていたら違うようだ。arrayではなくtupleのようだ
# output is ('/path/to', 'file') dir = "/path/to/file" print(os.path.split(dir))
プロパティ。システムに応じて自動でパスを判断してくれる。まあ自分は/dev/nullしか使うことないが
os.path.devnullosからも直接呼び出すことが可能
os.devnull
perlのそれと同じ。tupleで返って来る。unix timeを指定しなかった場合はcurrent timestampが指定されたことになる。perlのlocaltimeみたいに
my($sec, $min, $hour, $day, $mon, $year) = localtime time; $mon++; $year += 1900;としなくてよいのはいいことだ
# (2010, 9, 26, 1, 42, 10, 6, 269, 0) # windows環境だとstructで返ってきた # time.struct_time(tm_year=2010, tm_mon=9, tm_mday=26, tm_hour=1, tm_min=50, tm_sec=14, tm_wday=6, tm_yday=269, tm_isdst=0) time.localtime(<unix time>)
index | 値 | 意味 |
---|---|---|
6 | tm_wday | [0,6] の間の数、月曜が0 |
7 | tm_yday | [1,366] の間の数 |
8 | tm_isdst | 0, 1 または -1 |
localtimeの逆。最後の値はわからない場合は-1にしとくとよいらしい
# tupleかstruct time.mktime((2010, 9, 26, 1, 46, 44, 6, 269, -1))
tupleかstructの日付データから文字列に変換する
# 2010-09-26 01:57:07 time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())フォーマット詳細はhttp://pythonjp.sourceforge.jp/dev/library/time.ht...
strftimeの逆
# time.struct_time(tm_year=2010, tm_mon=9, tm_mday=26, tm_hour=1, tm_min=57, tm_sec=7, tm_wday=6, tm_yday=269, tm_isdst=-1) time.strptime('2010-09-26 01:57:07', "%Y-%m-%d %H:%M:%S")
現在の日付を返す
from datetime import datetime import time tm = time.localtime() d = datetime(tm.tm_year, tm.tm_mon, tm.tm_mday) # output is 2010-09-26 14:59:34.328000 print(d.today()) print(d.now())ちなみにdatetimeオブジェクトをそのまま出力することも可能
tm1 = time.localtime(time.time()) d1 = datetime(tm1.tm_year, tm1.tm_mon, tm1.tm_mday, tm1.tm_hour, tm1.tm_min, tm1.tm_sec) tm2 = time.localtime(time.time() - 3600) d2 = datetime(tm2.tm_year, tm2.tm_mon, tm2.tm_mday, tm2.tm_hour, tm2.tm_min, tm2.tm_sec) # output is True print(d1 > d2)
from datetime import datetime, date, timedelta import time t = time.localtime() d = datetime(t.tm_year, t.tm_mon, t.tm_mday) print(date.today() + timedelta(days=3)) print(d + timedelta(month=1))
timedelta
# -*- coding: utf-8 -*- from datetime import datetime, date, timedelta import time t1 = time.localtime() d1 = datetime(t1.tm_year, t1.tm_mon, t1.tm_mday, t1.tm_hour, t1.tm_min, t1.tm_sec) t2 = time.localtime(time.time() - 86400 * 10) d2 = datetime(t2.tm_year, t2.tm_mon, t2.tm_mday, t2.tm_hour, t2.tm_min, t2.tm_sec) # timedelta objectが返って来る td = d1 - d2 print(td.days) print(td.seconds)
指定した値が文字列の先頭にマッチするか調べる。あんまり使うことないかも
str = "nirvana" # これはマッチしない。Noneが返ってくるだけ re.match(r"a", str) # この場合はsre.SRE_Match object re.match(r"n", str)
perlだと
my $ip = "192.168.0.1"; if ($ip =~ /^(\d{1,})\.(\d{1,})\.(\d{1,})\.(\d{1,})$/) { # 192 my $first_segment = $1; # 168 my $second_segment = $2; }のようなことをpythonでする場合
ip = "192.168.0.1" match = re.search(r"^(\d{1,})\.(\d{1,})\.(\d{1,})\.(\d{1,})$", ip) if match: first_segment = match.group(1) second_segment = match.group(2)となる。ちなみにgroup(0)はマッチした文字列全体。つまりこの場合は192.168.0.1となる
1とか2でもいいんだけど。?P<group_name>でグループ名を定義することができる
ip = "192.168.0.1" match = re.search(r"^(?P<first_segment>\d{1,})\.(?P<second_segment>\d{1,})\.(?P<third_segment>\d{1,})\.(?P<fourth_segment>\d{1,})$", ip) match.group("first_segment")
置換。置き換え対象引数は関数でもいいっぽいがまあそこまで今のところは必要ないし
str = "I Love Nirvana. I Love Melvins." str = re.sub(r"Love", "Hate", str)
perlでいうqr//
ip = "192.168.0.1" regex = re.compile(r"^(\d{1,})\.(\d{1,})\.(\d{1,})\.(\d{1,})$") match = regex.search(ip) if match: first_segment = match.group(1) second_segment = match.group(2)
os.systemとおんなじっぽい
from subprocess import call ret = call(["ls", "-l"])終了コードが0以外が例外を発生したい場合はcheck_callを使う
check_call(["ls", "-l"])標準出力とかだしたくないって場合は
from subprocess import call import os null = open(os.path.devnull, mode="w") ret = call(["ls", "-l"], stdout=null, stderr=null) print("exit_value:%(ret)i" % locals())とすればよい
/path/to/command >/dev/null 2>&1ってのをしたいてきに使えそうだ。↑の例でいうとこのように書き直せる
from subprocess import call, STDOUT import os null = open(os.path.devnull, mode="w") ret = call(["ls", "-l"], stdout=null, stderr=STDOUT)
callとかとほぼほぼ同じ
from subprocess import Popen, PIPE proc = Popen(["ls", "-l"], stdout=PIPE, stderr=PIPE) # stdoutとstderrの結果をそれぞれ受け取る (stdout, stderr) = proc.communicate() # waitには終了コードの結果が返ってくる exit_value = proc.wait()こういう方法もある
from subprocess import Popen, PIPE proc = Popen(["ls", "-l"], stdout=PIPE, stderr=PIPE) # stdoutとstderrの結果をそれぞれ出力 for line in proc.stdout: print(line.rstrip()) for line in proc.stderr: print(line.rstrip()) # waitには終了コードの結果が返ってくる exit_value = proc.wait()
shellでいう、こういうことを直接できなさそうなので
cat /etc/passwd | grep "holly"この場合、こうするとよい
p1 = Popen(["cat", "/etc/passwd"], stdout=PIPE) p2 = Popen(["grep", "holly"], stdin=p1.stdout, stdout=PIPE, close_fds=True) res = p2.communicate()[0]
ありきたりなのだとsendmailとか
from subprocess import Popen, PIPE proc = Popen(["/usr/sbin/sendmail", "-t", "-i"], stdin=PIPE) proc.stdin.write("From: root@localhost.localdomain\n") proc.stdin.write("To: root@localhost.localdomain\n") proc.stdin.write("Subject: hello\n") proc.stdin.write("\n") proc.stdin.write("test\n") proc.stdin.close() proc.wait()
こういうことをする
試してみたらいけた
cat <<EOL | sort -u melvins akiko muse nirvana akiko marsvolta converge melvins EOL
試してみたらいけた
from subprocess import Popen, PIPE list = ["melvins", "akiko", "muse", "nirvana", "akiko", "marsvolta", "converge", "melvins"] proc = Popen(["sort", "-u"], stdout=PIPE, stdin=PIPE) for word in list: proc.stdin.write("%s\n" % (word)) for line in proc.stdout: print(line.rstrip()) proc.wait()結果は
akiko converge marsvolta melvins muse nirvana
killはSIGKILL、terminateはSIGTERMらしい。試してないけど
proc = Popen(["sort", "-u"], stdout=PIPE, stdin=PIPE) proc.kill() #proc.terminate()
SIGTERMとかSIGINTとか。kill -lで使用できるシグナルはすべて使用できると思う
from signal import SIGTERM, SIGINT import sys, os pid = sys.argv[1] os.kill(int(pid), SIGTERM)
タイマー。時間を越えるとSIGALRMが発生する
import timer, signal signal.alarm(10) # anything to heavy execute doing... signal.alarm(0)
シグナルハンドラー。上記例にSIGALRM発生時に発動する関数を定義すると
import os, sys, time, signal from signal import SIGTERM, SIGINT, SIGALRM def handler(signum, frame): print("signum:%i received" % (signum)) sys.exit(1) signal.signal(SIGALRM, handler) signal.alarm(3) # anything to heavy execute doing... signal.alarm(0) sys.exit(0)とすると3秒後にsignum:14 receivedと表示され、終了コードは1となる。signal無視の場合はこうなる
import os, sys, time, signal from signal import SIGTERM, SIGINT, SIGALRM, SIG_IGN, SIG_DFL # Ctrl + Cによる割り込みを無視 signal.signal(SIGINT, SIG_IGN)デフォルトの動作にあわせる場合はこうなる
signal.signal(SIGINT, SIG_DFL)
shellが起動することになるのか?どうかはさておき、os.listdir()と違って条件にマッチしたものだけ取り出すことができる
import glob for entry in glob.glob("/path/to/dir/*.tar.gz"): print(entry)
perlでいうGetopt::Long。とりあえず以下のソースをはっつけておけばだいたいできると思う
import sys from optparse import OptionParser def my_callback(option, opt, value, parser): parser.values.c = value usage = "usage: %prog [options] arg1 arg2...argN" version = "%prog 1.0" parser = OptionParser(usage=usage, version=version) parser.add_option("-f", "--file", dest="file", help="write report to FILE", metavar="FILE" ) parser.add_option("-m", "--mode", dest="mode", default="intermediate", help="interaction mode: novice, intermediate, or expert [default: %default]", metavar="MODE" ) parser.add_option("-n", "--num", type="int", help="num value", metavar="NUM" ) parser.add_option("-t", "--tracks", action="append", help="track balues", metavar="TRACK" ) parser.add_option("-c", action="callback", callback=my_callback, type="string", help="callback execute" ) parser.add_option("--verbose", "-v", action="store_true", dest="verbose", default=False, help="print status messages to stdout" ) parser.add_option("-q", "--quiet", action="store_true", dest="quit", default=False, help="don't print status messages to stdout" ) options, args = parser.parse_args() if (len(args) == 0): parser.error("incorrect number of arguments") # anything to do...
-h or --helpで自動的に表示してくれる
cmd.py -h Usage: cmd.py [options] arg1 arg2...argN Options: --version show program's version number and exit -h, --help show this help message and exit -f FILE, --file=FILE write report to FILE -m MODE, --mode=MODE interaction mode: novice, intermediate, or expert [default: intermediate] -n NUM, --num=NUM num value -t TRACK, --tracks=TRACK track balues -c C callback execute -v, --verbose print status messages to stdout -q, --quiet don't print status messages to stdoutUsageがでるようにしているのはOptionParserの引数にusageを定義する必要がある
usage = "usage: %prog [options] arg1 arg2...argN" version = "%prog 1.0" parser = OptionParser(usage=usage, version=version)
--versionで自動的に表示してくれる。usageと同じでOptionParserの引数にversionの定義をする必要がある
cmd.py --version cmd.py 1.0
オプションを定義する。使い方はだいたいみたらわかるので、必要そうなのだけ
**kwargs | 意味 |
---|---|
opts | 必ず1個 or 2個必要。-qとか--quitとか |
dest | parser_args()で返されるoptions(dict)に保存される値に対応するkeyの名前 |
action | デフォルトはstore。その他store_true, store_false, append, callbackなど |
type | デフォルトはstring。その他int, floatなど |
help | --helpの時に表示されるオプションのメッセージ |
metavar | 引数が必須のオプションの--helpの時に表示されるオプションの引数の説明用文字列 |
default | 引数が必須のオプションで指定がない場合に適用される値 |
callback | actionがcallbackの場合に指定する関数ポインタ。lambdaでもよい |
同じオプションを複数指定することができる
cmd.py --tracks="one" --tracks="two" --tracks="three" argsとすると
options, args = parser.parser_args() # output is ['one', 'two', 'three'] print(options.tracks)とlistで受け取ることができる
独自でオプションが指定された場合の処理を行いたい時とか。そんなに出番がなさそうだが。add_optionで
parser.add_option("-c", action="callback", callback=my_callback, type="string", help="callback execute" )とありmy_callbackで
def my_callback(option, opt, value, parser): parser.values.c = valueとしているので、
cmd.py -c hello argsと実行すると
options, args = parser.parser_args() # output is hello print(options.c)となる。add_optionでtype="string"のように引数の型を指定しない場合は-c helloのように指定しても値は取得できない。オプションが指定されたかどうかの情報しかわたってこない。callbackの引数のoption or optにオプションの文字列(この場合だと-c)のみがわたってくる
Data::Dumperみたいのか
import pprint pp = pprint.PrettyPrinter(indent=4) dictionary = { "one": "いち", "two": "に", "three": "さん" } pp.pprint(dictionary) # evalはいらんかも print(eval('"""%s"""' % pp.pformat(dictionary)))
listをランダムに順番を入れ替える
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] random.shuffle(nums) # nums is [5, 9, 4, 6, 1, 7, 3, 2, 10, 8]
opt | 説明 |
---|---|
lineterminator | 改行。デフォルトは\r\n |
delimiter | 区切り文字。デフォルトは, |
import csv reader = csv.reader(open("hello.csv", mode="r")) for row in reader: # 配列で返って来る i, name = row print("No.%i: %s" % (int(i), name))
import csv writer = csv.writer(open("hello.csv", mode="a"), lineterminator="\n") writer.writerow([4, "alice in chains"]) writer.writerow([5, "akiko"])
gethostbynameの逆。tupleで返ってくる
socket.gethostbyaddr("74.125.95.104") # ('iw-in-f104.1e100.net', [], ['74.125.95.104'])
conn = http.client.HTTPConnection("www.yahoo.co.jp", 80) conn.request( "GET", "/" ) # res is http.client.HTTPResponse res = conn.getresponse() # res.statusはgetcode()でもよい print(res.status, res.reason) # bodyはbyte型で戻ってくるのでdecodeする print(res.readall().decode("utf-8")) res.close()
bodyを分割して取得するなら
while True: buf = res.read(80) if len(buf) == 0: break print(buf.decode("utf-8"))
import http.client import urllib.parse param = { 'hl' : 'ja', 'source' : 'hp', 'biw' : 1009, 'bih' : 734, 'q' : 'くま', 'btnG' : 'Google 検索', 'aq' : 'f', 'aqi' : 'g-r10', 'aql' : "", 'oq' : "", 'gs_rfai' : "" } param_str = urllib.parse.urlencode(param) conn = http.client.HTTPConnection("www.google.co.jp", 80) conn.request( "GET", "/search", param_str ) res = conn.getresponse() print(res.status, res.reason) print(res.readall().decode("utf-8")) res.close()パラメータを送る指定を追加すればよい。POSTで送信時はGETの部分をPOSTにしてあげればよい
req/resの内容が見れる
conn = http.client.HTTPConnection("www.google.co.jp", 80) conn.set_debuglevel(1)
HTTPConnection#requestの第4引数にdictで渡す
# example conn.request( "GET", "/search", param_str, {'X-Custom-Header': 'DEBUG'} )
HTTPReponse#getheaders()
res = conn.getresponse() for header in res.getheaders(): # tupleで返ってくる print("%s: %s" % header) # output is # Content-Type: text/html; charset=UTF-8 # Content-Length: 1350 # Date: Mon, 18 Oct 2010 15:57:23 GMT # Server: GFE/2.0個別に取得するなら
print(res.getheader("Content-Length", ""))
perlでいうDigest::MD5
from hashlib import md5 import sys m = md5() with open(sys.argv[0], mode="r") as f: blob = f.read() m.update(blob.encode("utf-8")) print(m.hexdigest())もちろんmd5sumで実行ファイルを引数に渡して同じになるか確認してみるとよい
コマンドがインストールされる。たぶん直接pyflakesのライブラリ本体を扱うことはあまりないようだ。python3.x系ではインストールすらできない
#!/usr/bin/python import resource import fcntl import signal from signal import SIGTERM, SIGINT from resource import RUSAGE_SELF def handler(sig, flame): print "SIGNAL[%i] received" % sig signal.signal(SIGTERM, handler) signal.signal(SIGINT, handler) data = [] f = open("test.txt", mode="w") fcntl.flock(f, fcntl.LOCK_EX) for i in range(0, 10000): data.append("a" * i) f.write(str(i) + "\n") f.close() print resource.getrusage(RUSAGE_SELF)こんなソースがあるとして
pyflakes getrusage.pyとして何も表示されなければ、それで問題ない。例えば
#signal.signal(SIGTERM, handler) #signal.signal(SIGINT, handler)のようにはじめの例からコメントにした状態でpyflakesを実行すると
./getrusage.py:5: 'signal' imported but unused ./getrusage.py:6: 'SIGINT' imported but unused ./getrusage.py:6: 'SIGTERM' imported but unusedのように警告を出してくれる
指定バージョン
easy_install "<pkg> == 1.1"指定バージョン以下
easy_install "<pkg> < 1.1"とか。http://dsas.blog.klab.org/archives/51284317.htmlとかにもうちょい詳しく載っている
機能としてはないので
# $PYTHONLIB = /usr/lib/pythonx.x 自分でコンパイルしていれたのなら/usr/local/lib/pythonx.x grep -E "\.egg$" $PYTHONLIB/site-packages/easy-install.pth ./pyflakes-0.4.0-py2.4.egg ./pip-0.8.1-py2.4.egg ./psycopg2-2.3.0_beta1-py2.4-linux-i686.eggとすると一覧表示される
mで。例えばpipの場合
easy_install -m pip Searching for pip Best match: pip 0.8.1 Processing pip-0.8.1-py2.4.egg Installing pip script to /usr/bin Installing pip-2.4 script to /usr/bin Using /usr/lib/python2.4/site-packages/pip-0.8.1-py2.4.egg Because this distribution was installed --multi-version, before you can import modules from this package in an application, you will need to 'import pkg_resources' and then use a 'require()' call similar to one of these examples, in order to select the desired version: pkg_resources.require("pip") # latest installed version pkg_resources.require("pip==0.8.1") # this exact version pkg_resources.require("pip>=0.8.1") # this version or higher Processing dependencies for pipこれでインストール時の情報が表示されるので、$PYTHONLIB/site-packages/easy-install.pthから該当するパッケージのegg情報を削除する。あとはここに表示された関連するファイル(eggと実際にインストールされたファイルと)を削除すればよい。--record をインストール時に指定していた場合は指定したファイルにすべてインストールパスが記録されるので、
cat record.txt | xargs rm -frとすればよい
タグ
最新コメント