catコマンド
ファイルの中身を表示する
1 2 3 4 5 |
$ cat file abc xyz $ |
以下のようにしても動く
1 2 3 4 5 |
$ cat < file abc xyz $ |
これは標準入力からファイルの内容を受け取りそれを出力している。
標準入力での切り分け
上記のことをpythonで実現するにはまず標準入力が普通の標準入力(キーボード)かパイプかを調べて処理を分ける。
1 2 3 4 5 6 7 8 |
import sys if sys.stdin.isatty(): # keyboard pass else: # pipe pass |
ttyとはteleprinterのことで、昔あった手元にあるキーボードから遠くのプリンターに文字をタイプするマシンのことらしい。Linux界では接続されているキーボードをttyという概念で認識する。
ttyだった場合、引数にファイルがあればそれを表示し、なければ標準入力(キーボード)から入力を受け付ける。
ファイルの処理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import sys import os def docat(file=sys.stdin): sys.stdout.write(file.read()) if sys.stdin.isatty(): # keyboard if len(sys.argv) > 1: file = open(sys.argv[1]) docat(file) file.close() pass else: # pipe pass |
パイプの処理
標準入力がキーボードで引数がない場合やパイプの場合はstdinから読み込む
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import sys import os def docat(file=sys.stdin): sys.stdout.write(file.read()) if sys.stdin.isatty(): # keyboard if len(sys.argv) > 1: file = open(sys.argv[1]) docat(file) file.close() else: docat() else: # pipe docat() |
これで大体動くが、catとの違いはキーボードから入力したときのエコーバックの時期が違う。catは1行ごとにエコーバックするが、このスクリプトの場合、Ctrl+D(Linux)やCtrl+Z(Windows)でEOFが送られるか、バッファがいっぱいになるまでwriteへ処理がいかない。よってこれを直す。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import sys import os def docat(file=sys.stdin): while True: line = file.readline() if not line: break sys.stdout.write(line) if sys.stdin.isatty(): # keyboard if len(sys.argv) > 1: file = open(sys.argv[1]) docat(file) file.close() else: docat() else: # pipe docat() |
考察
キーボードとパイプの両方から入力を受け付けると便利なことが多い。スクリプトがjsonファイルを受け付けるとき、ユーザー側には他の形式のファイルがあってそれをjsonに変換してスクリプトの入力としたい時などパイプ入力がないといったんファイルに変換しないとならなくなる。