[python][unix] リダイレクションやパイプの有無を識別する方法

リダイレクションやパイプを使用している場合は読み込み、使用してない場合はヘルプを表示するなど、
切り替えたい場合は次のように書くのがいいのかな。

import os
import sys

if os.isatty(os.sys.stdin.fileno()):
    print_usage()
    sys.exit(1)

# 標準入力をすべて読み込む
for line in sys.stdin:
    sys.stdout.write(line)

リダイレクションやパイプと、端末の入力を同時に行う方法

お題

リダイレクションやパイプからデータを読み込んだ上で端末の入力を読み込む、
そんなプログラムを書こうとしてはまったので整理。

イメージ:

$ echo hoge foo bar | ./to_upper_case_and_xxx.py
HOGE FOO BAR
input:  <- 端末からなにか入力

この場合、次のように書きたくなります。

import sys

# 標準入力をすべて読み込む
for line in sys.stdin:
    sys.stdout.write(line.upper())

# 端末の入力を読み込む
result = raw_input('input: ')
print '>> ' + result

しかし、EOFErrorが発生してうまくいきません。

$ echo hoge | ./raw_input_test.py 
...
EOFError: EOF when reading a line

raw_input()が内部でsys.stdinを参照しているので、読みきってると動かないわけですね。
それに読み込み元がリダイレクションやパイプになっているため、このままではインタラクティブな操作はできません。

ではどうするか

実行例 標準入力
通常 ./program.py /dev/tty
パイプ echo hoge | ./program.py echoの出力
リダイレクション ./program.py < path/to/file /path/to/file

通常、標準入力は/dev/ttyになっており、端末の入力はこの特殊なファイルから読み込んでいます。
これが、パイプやリダイレクションを利用した場合、別プログラムの出力やファイルに差し替わります。

/dev/ttyをオープンすれば端末からの入力を受け取れるので、sys.stdinに割り当ててみましょう。

修正版

というわけで、次のように変更します。

import os
import sys

# 標準入力をすべて読み込む
for line in sys.stdin:
    sys.stdout.write(line.upper())

# TTY (/dev/tty) をオープンして、標準入力に割り当てる
sys.stdin = file('/dev/tty')

# TTYを割り当てたので、端末の入力を読み込める
result = raw_input('input: ')
print '>> ' + result

今度はちゃんと動作しますね。

もちろん、raw_input()を使わずに直接TTYから読み込んでもOKです。

tty = file(os.ctermid())
sys.stdout.write('input: ')
result = tty.readline()
print '>> ' + result

Gitでコミットをやり直す方法 (パッチ編)

う〜ん、困った。
あるコミットはUTF-8、またあるコミットはEUC-JPでログを書くなんて…。

修正しようにも

$ git rebase -i

では文字化けてしてまってうまくいかない。

ちょっと大げさかもしれないけど、次の方法で修正する。

既存コミットのパッチに書き出す

第1引数にはパッチを作成し始めるコミットを指定する。
今回はすべて書き出すのでmasterを指定。

$ git format-patch master

コマンドを実行するとその場にパッチを作成しようとするので要注意。
patchesなど適当なディレクトリに移動しておくべし。

問題のパッチを修正

さて、以下のようなパッチファイルができた。

$ ls -1
0001-FIXME-xxx-xxx-xxx.patch
0002-FIXME-xxx-xxx-xxx.patch
0003-FIXME-xxx-xxx-xxx.patch
0004-FIXME-xxx-xxx-xxx.patch
0005-FIXME-xxx-xxx-xxx.patch
0006-FIXME-xxx-xxx-xxx.patch
〜 略 〜

中身はメールと同じ書式で、コミットログとファイルの差分が含まれる。
ここでは、問題のあるコミットログを修正する。

新しいブランチにパッチを適用

まずはパッチを適用する新しいブランチを作成、チェックアウト。
masterからのパッチなのでmasterを元にする。

$ git branch new_branch master
$ git checkout new_branch

最後にamコマンドでパッチを適用。

$ git am *.patch

おしまい♪

一般ユーザでscreenが使えない

ServersMan@VPS(CentOS5)で一般ユーザだとscreenが使えない。

Sorry, could not find a PTY.

なんて言われちゃいます。

一般ユーザで screen が動かない - プログラマ 福重 伸太朗 〜基本へ帰ろう〜によると
/dev/ptmxのパーミッションが足りないようだ。

$ ls -la /dev/ptmx
crw-r--r-- 1 root root 5, 2  2?? 13 12:43 /dev/ptmx

なのでグループとその他に書き込み権限を与えてあげる。

# chmod 0666 /dev/ptmx
# ls -la /dev/ptmx 
crw-rw-rw- 1 root root 5, 2  2?? 13 12:43 /dev/ptmx

するとちゃんと動いた♪