2019年1月2日 2022年5月26日
Python@CentOS6 の標準出力と readline の絡みでハマったことがあったので、いろいろ調べた記録を残しておきます。
再現実験
テスト用に以下のようなスクリプトを作ってみます。
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import readline
print raw_input("")
そして以下のような3種のテストを実施してみますと、
$ # ↓実行環境
$ cat /etc/centos-release && python --version
CentOS release 6.7 (Final)
Python 2.6.6
$
$ # ↓テスト1(OK, ターミナル出力)
$ ./test.py
aaaa(<-入力文字)
aaaa(<-出力文字)
$
$ # ↓テスト2(NG, リダイレクト)
$ ./test.py > /tmp/hoge
aaaa(<-入力文字)
$ xxd /tmp/hoge
0000000: 1b5b 3f31 3033 3468 6161 6161 0a .[?1034haaaa.
$
$ # ↓テスト3(NG, パイプ)
$ ./test.py | xxd
aaaa(<-入力文字)
0000000: 1b5b 3f31 3033 3468 6161 6161 0a .[?1034haaaa.
標準出力先がターミナル以外の場合は、想定していない文字列(エスケープシーケンス
ESC[?1034h
)が先頭に紛れ込んでしまってます。このゴミは実際は
import readline
を実行した直後に出力されているので、
$ python -c 'import readline' | xxd
0000000: 1b5b 3f31 3033 3468 .[?1034h
だけでも動作の可否が確認できます。
対処1: import readline
を使わない
readline を使わない場合は
input()
や
raw_input()
による文字入力時にカーソルキーが効かないなどのデメリットがありますが、それが問題にならないなら
import readline
を省いてしまったほうがてっとり早いです。
対処2: 環境変数 TERM
を一時的に変えてみる
どうしても readline を使いたいのならば、一時的に環境変数
TERM
を変更する、というテもあります。環境変数
TERM
の中身が
xterm
以外だとほぼ大丈夫なようです。
$ echo $TERM
xterm
$ python -c 'import readline' | xxd
0000000: 1b5b 3f31 3033 3468 .[?1034h
$ TERM=vt100 python -c 'import readline' | xxd
$ TERM= python -c 'import readline' | xxd
他の対処法は?
上記のいずれかで対処はできるのですが、他に方法がないものかどうか調べてみました。
この問題は Python 2.7.9 にて初めて着手されたようです。
+++++++++++
Python News
+++++++++++
What's New in Python 2.7.9 release candidate 1?
===============================================
*Release date: 2014-11-25*
(略)
- Issue #19884: readline: Disable the meta modifier key if stdout is not
a terminal to not write the ANSI sequence "\033[1034h" into stdout. This
sequence is used on some terminal (ex: TERM=xterm-256color") to enable
support of 8 bit characters.
CentOS6 では centos-sclo-rh リポジトリから Python 2.7.13 が提供されているので、それなら大丈夫だろうと思って試してみると・・・
$ scl enable python27 bash
$ cat /etc/centos-release && python --version
CentOS release 6.7 (Final)
Python 2.7.13
$
$ python -c 'import readline' | xxd
0000000: 1b5b 3f31 3033 3468 .[?1034h
予想に反して、何故か相変わらずゴミが出てしまいます。
readline のバージョンで挙動が変わる
Python 2.7.9 の対策は、readline の初期化時に
標準出力先がパイプやリダイレクトの場合は enable-meta-key
の設定値を off
にする というものですが、readline のバージョンによっては
enable-meta-key
自体が実装されてなかったりします。これが初めて実装されたのは readline 6.1 のようです。
readline/NEWS
This is a terse description of the new features added to readline-6.1 since
the release of readline-6.0.
1. New Features in Readline
(略)
i. New bindable variable: enable-meta-key. Controls whether or not readline
sends the smm/rmm sequences if the terminal indicates it has a meta key
that enables eight-bit characters.
CentOS6 に入っている readline のバージョンは 6.0 になので、readline のバージョンを上げない限りどの Python のバージョンでもゴミが表示されることになりそうです。
$ ldconfig -v | grep libreadline
libreadline.so.6 -> libreadline.so.6.0
readline をバージョンアップしてみる
果たして readline が 6.1 であれば解決するのでしょうか? ということで、実験してみます。
ビルドの流れはざっと以下のとおりです。今回は共有ライブラリのみで充分なので、
configure
に
--disable-static
(静的ライブラリを作成しない)を付けています。
$ # ↓ダウンロード&ビルド
$ wget http://git.savannah.gnu.org/cgit/readline.git/snapshot/readline-86cfd01f1d547422a1fb0365719491a355847dc0.tar.gz
$ tar xvf readline-86cfd01f1d547422a1fb0365719491a355847dc0.tar.gz
$ cd readline-86cfd01f1d547422a1fb0365719491a355847dc0
$ ./configure --disable-static
$ make
$ sudo make install
$
$ # ↓ライブラリの読み込み先を一時的に追加して反映
$ sudo sh -c 'echo /usr/local/lib > /etc/ld.so.conf.d/test.conf'
$ ldconfig -v | grep -E '(^/|libreadline)'
/usr/local/lib:
libreadline.so.6 -> libreadline.so.6.1
/lib64:
libreadline.so.6 -> libreadline.so.6.0
(略)
$
$ # ↓Python2.6 でテスト (NG)
$ python --version && python -c 'import readline' | xxd
Python 2.6.6
0000000: 1b5b 3f31 3033 3468 .[?1034h
$
$ # ↓Python2.7 でテスト (OK)
$ scl enable python27 bash
$ python --version && python -c 'import readline' | xxd
Python 2.7.13
$
$ # ↓Python2.6 の環境に戻す
$ exit
$
確かに readline 6.1 にすると、Python 2.7.13 ではゴミが発生しないことが確認できました。
では、readline を元のバージョンに戻しておきます。
$
$ # ↓ライブラリの読み込み先を元に戻して反映
$ sudo rm /etc/ld.so.conf.d/test.conf
$ ldconfig -v | grep -E '(^/|libreadline)'
/lib64:
libreadline.so.6 -> libreadline.so.6.0
(略)
$
$ # ↓readline 6.1 のアンインストール
$ sudo make uninstall
$
$ # ↓readline 6.0 の下でもう1回ずつテスト
$ python --version && python -c 'import readline' | xxd
Python 2.6.6
0000000: 1b5b 3f31 3033 3468 .[?1034h
$
$ scl enable python27 bash
$ python --version && python -c 'import readline' | xxd
Python 2.7.13
0000000: 1b5b 3f31 3033 3468 .[?1034h
$
まとめ
Python の
import readline
によるゴミ表示の(おそらく)正当な対策は
Python は 2.7.9 以上 readline は 6.1 以上
の両方が必要、ということだろうと思います。
・・・が、CentOS7 では
$ cat /etc/centos-release && python --version
CentOS Linux release 7.3.1611 (Core)
Python 2.7.5
$ ldconfig -v | grep -E '(^/|libreadline)'
/lib64:
libreadline.so.6 -> libreadline.so.6.2
(略)
$
$ python -c 'import readline' | xxd
$
readline のバージョンこそ 6.2 ですが、Python は 2.7.9 未満でありながら正常に動いてしまってます。
これについてはまた
別記事 で。