Python Tips: 標準入力がどのように渡されているのかをチェックしたい

今回は Python で標準入力を扱う際に標準入力がどのように渡されているのかをチェックする方法についてご紹介したいと思います。

標準入力の渡し方は大きく分けて、ファイルからのリダイレクトやパイプによって渡す場合とキーボードからインタラクティブに渡す場合の 2 通りに分けることができます。

例えば、リダクレクトやパイプのときにだけ処理を行いたいような場合は次のようなコードを書くことになります。

import sys

if (標準入力がキーボードから渡されている):
    sys.stderr.write("キーボードからの標準入力には対応していません。\n")
    exit()

(やりたい処理)

ここで「(標準入力がキーボードから渡されている)」のところは具体的にどのように書けばいいのでしょうか。早速結論ですが、こちらは sys.stdin の isatty() メソッドを使えば OK です。

import sys

if sys.stdin.isatty():
    sys.stderr.write("キーボードからの標準入力には対応していません。\n")
    exit()

isatty() は読んでそのまま「 is a tty 」の意味らしく、標準入力がキーボードからの入力の場合(あるいは標準入力に何も渡されていない場合)は True を返します。ファイルのリダイレクトやパイプの場合には False を返します。

サンプルを見てみましょう。

check_stdin.py:

# coding: utf-8
import sys

if sys.stdin.isatty():
    sys.stderr.write('パイプあるいはリダイレクトで標準入力を渡してください。\n')
else:
    sys.stderr.write('標準入力をそのまま標準出力に流します。\n')
    sys.stdout.write(sys.stdin.read())

こちらを使うと次のようになります。

$ python check_stdin.py
パイプあるいはリダイレクトで標準入力を渡してください。
$ echo 'hello' | python check_stdin.py
hello
標準入力をそのまま標準出力に流します。

標準入力の渡し方を識別できていることがわかります。

以下はもう少し実用的なサンプルで、 csv 形式のテキストを標準入力から受け取る例です。

read_csv.py:

# coding: utf-8

"""標準入力から csv を読む
"""

import sys
import csv
from itertools import islice

def main():
    """標準入力で与えられた csv を読み込む

    - 最初の 5 行だけ、ヘッダーなしで読み込む
    """
    rows, header = csv_read_stdin(5, True)

    print("Rows: {}".format(list(rows)))

def csv_read_stdin(number, is_headerless):
    """標準入力から csv を読み込む
    """
    if sys.stdin.isatty():
        sys.stderr.write("標準入力はパイプまたはリダイレクトで渡してください。\n")
        exit()
    reader = csv.reader(sys.stdin)
    header = [] if is_headerless else next(reader)
    rows = islice(reader, number)

    return rows, header

if __name__ == "__main__":
    main()

試してみます。

$ python read_csv.py
標準入力はパイプまたはリダイレクトで渡してください。
$ python read_csv.py <<EOS
> Takeda,Shingen
> Takeda,Katsuyori
> EOS
Rows: [['Takeda', 'Shingen'], ['Takeda', 'Katsuyori']]

正しく識別できていることが確認できました。

以上です。シンプルでわかりやすいですねー。