created: 2021-01-31T02:17:59.000Z
pipeされた標準入力がseekできなかった話
こんなコードを書こうとした。
if filepath == '-':
fo = sys.stdin
else:
fo = open(filepath, mode=read_mode)
ファイルパスの引数に -
を渡したら標準入力の内容を使うというもの。
標準入力のシノニムとして -
を使うコードは python の fileinput や awscli, gsutil にもあったものなのでいいかなと思ったのだが、後続の処理で fo.seek(0)
が呼ばれてエラーになることがわかった。
# pipeすると fo.seek(0) で IOError: [Errno 29] Illegal seek になる
$ cat /tmp/_.csv | ./csv2sqlite.py - /tmp/_.sqlite3
Traceback (most recent call last):
File "./csv2sqlite.py", line 210, in <module>
convert(args.csv_file, args.sqlite_db_file, args.table_name, args.headers, compression, args.types)
File "./csv2sqlite.py", line 47, in convert
fo.seek(0)
IOError: [Errno 29] Illegal seek
pythonのstdinが抽象化してくれていると勝手に思っていたが、調べてみるとpipeやsocket由来の標準入力はseekできないルールがちゃんとあり、そのルールに抵触しているのでエラーになっているようだった。
If it's a terminal or a pipe or a socket, no. If it's a file, yes (usually).
OSにもよるのかもしれない (Linuxはダメ)
Some devices are incapable of seeking and POSIX does not specify which devices must support lseek().
ファイル由来の標準入力だとseekできる
$ ./csv2sqlite.py - /tmp/_.sqlite3 < /tmp/_.csv