Pythonで学ぶ「詳解 UNIXプログラミング」(その1) 第1章 概論
1.1 ディレクトリ内の全てのファイルをリストする
import sys, os if len(sys.argv) != 2: sys.exit("a single argument (the directory name) is required") try: filenames = os.listdir(sys.argv[1]) except OSError: sys.exit("can't open {0}".format(sys.argv[1])) for filename in filenames: print filename sys.exit(0)
Pythonでは、opendir()/readdir()/closedir()
を個別に呼び出す必要はなく、os.listdir()
だけでファイル一覧を取得することができる。ただし、readdir()
と違って、os.listdir()
では親ディレクトリとカレントディレクトリを示す" . "
と" .. "
を含まない。
エラーの検出は、os.listdir()
の戻り値をチェックするのではなく、Pythonの例外処理で検出する。ここで、例外ハンドラを
except:
...
ではなく、
except OSError: ...
としている点に注意して欲しい。except:〜
の形式では全ての例外を捕捉してしまうため、例えばos.listdir()
内でsys.exit()
が呼び出されたり、Ctrl+Cを打ってシグナルが発生した場合なども、except:
節が実行され、エラーメッセージが表示されてしまう。特に必要がなければ、 except:
ではなく、except OSError:
のように例外クラス名を指定するようにしよう。
具体的な例外クラス名を調べるのがめんどくさければ、except Exception:
でもシグナルやsys.exit()
の例外を捕捉せずに終了するとこができる。
このスクリプトでは、エラー表示と終了状態の設定をsys.exit()
で行っている。終了状態が1
ならば、この方法が一番簡単だろう。正常終了した場合には、sys.exit(0)
のように、終了状態として0
を指定して終了している。
1.2 低レベルファイルIOを使って、標準入力から標準出力にファイルをコピーする
import os, sys STDIN_FILENO = 0 STDOUT_FILENO = 1 BUFSILZE = 8192 while True: try: buf = os.read(STDIN_FILENO, BUFSILZE) except Exception: sys.exit("read error") if not buf: break try: os.write(STDOUT_FILENO, buf) except Exception: sys.exit("write error") sys.exit(0)
ファイル記述子を使った低レベルファイルIOは、os.read()/os.write()
で行う。
1.3 標準入出力を使用したファイルのコピー
import sys while True: try: c = sys.stdin.read(1) except Exception: sys.exit("read error") if not c: break try: sys.stdout.write(c) except Exception: sys.exit("write error") sys.exit(0)
C言語によるサンプルではgetc()/putc()
を使っているが、Pythonには相当する関数がないため、通常のファイル読み書きで1バイトずつ処理している。
1.4 プロセスのプロセスIDを出力する
import os print "Hello world from process ID {0}".format(os.getpid())
1.5 標準出力からコマンドを読み込み、実行する
import sys, os while True: try: buf = raw_input("% ") except EOFError: break try: pid = os.fork() except Exception: sys.exit("fork error") if pid == 0: # child try: os.execlp(buf, buf) except Exception: print >>sys.stderr, "couldn't execute" sys.exit(127) # parent try: os.waitpid(pid, 0) except Exception: sys.exit("waitpid error")
このサンプルのように、対話的に一行ずつ入力を受け取りたい時には、sys.stdin
オブジェクトを直接使用するよりも、raw_input()
を使用する。raw_input()
はプロンプトを出力してくれるだけでなく、ターミナル等からの読み込みなら readline
を使った行編集なども行ってくれるのだ。
1.6 エラー処理
通常、Pythonでエラーが発生した場合には例外が発生し、エラーメッセージは例外オブジェクトに含まれている。
try: open("ThisFileNeverExisted") except Exception, e: print str(e)
os.strerror()
で、エラー番号からエラーメッセージを取得することもできる。
import os, errno print os.strerror(errno.ENOENT)
1.7 ユーザ識別
import os print "uid = {0}, gid = {1}".format(os.getuid(), os.getgid())
1.8 シグナル
先ほど作成した単純なシェルを、割り込みキーを検出するように修正してみよう。
import sys, os import signal def sig_int(signum, frame): print "interrupt" signal.signal(signal.SIGINT, sig_int) while True: try: buf = raw_input("% ") except EOFError: break try: pid = os.fork() except Exception: sys.exit("fork error") if pid == 0: # child try: os.execlp(buf, buf) except Exception: print >>sys.stderr, "couldn't execute" sys.exit(127) # parent try: os.waitpid(pid, 0) except Exception: sys.exit("waitpid error")
APUEのサンプルをそのままPythonに置き換えると上のようになる。しかし、実行してみると、このスクリプトはC言語によるサンプルとはちょっと違った動作になってしまう。
C言語版ではテキストの読み込みにfgets()
を使用しており、シグナルハンドラが設定されている場合は、入力待ち状態でCtrl-Cを押しても読み込みが続行される。しかし、Python版ではEOFError
例外が発生し、処理が終了してしまう。Pythonインタープリタは独自にシグナルハンドラの起動を管理しており、C言語版と全く同じ動作にすることは難しい。
単にキーボード割り込みを無視して処理を継続するのが目的なら、SIGINT
割り込みで発生するKeyboardInterrupt
例外を利用しても良い。
import sys, os while True: try: buf = raw_input("% ") except KeyboardInterrupt: # ignore SIGINT continue try: pid = os.fork() except Exception: sys.exit("fork error") if pid == 0: # child try: os.execlp(buf, buf) except Exception: print >>sys.stderr, "couldn't execute" sys.exit(127) # parent try: os.waitpid(pid, 0) except Exception: sys.exit("waitpid error")