Windows と linux で同じPythonスクリプトを使いまわすときの注意点

年末年始は長めに休みを取って実家にゆっくり帰省してきました。

その際、以前エントリに書いたとおり、Windows に Pyscriptor をインストールして持ち帰り、趣味グラミングを楽しんでおりました。親がナンプレにはまっていたので、ナンプレを自動で解くプログラムを書いてみたり。

余談ですが、親の持っていた専門誌に載っていた問題は難易度が5段階に設定されており、難易度2あたりまでは完全に理詰めで「ここにはこの数字しか入らない」を順に埋めていけば解けるようになっていました。また、最高難易度の物でも、仮定を高々1段置く、つまり、「理詰めで解く」→「どこかのマスの数字を仮定したうえで理詰めで解いたら矛盾が出る物を探す」→「そのマスにその数字は入らないことが判明」→(最初に戻る) を繰り返すだけで解けるようになっていました。上記の手順では解けないけど答えは一意に確定する問題も、理論的には存在するはずですが、それでは人間が解けない難易度だ、という判断なのでしょうね。

閑話休題

そんなこんなで windowspython スクリプトを書いたわけですが、普段使いのLinux環境でそのコーディングの続きをしようとしたところ、なぜか動かなかったわけです。今日はそんな事にならないための対策と、そんな事になった時の修正方法の紹介です。

なにが原因なのか

原因は2つあります。いずれもテキストの形式の問題で、最初の1行にゴミが付いてしまうことが直接の原因です。

Python スクリプトの1行目には大抵こんなふうに書いていると思います。

#!/usr/bin/env python

この行をlinuxが正常に認識できないと、このファイルを python スクリプトとして扱ってはくれません。ところが、Windows 側の設定によっては、この行の頭とお尻にゴミが付くことがあるのです。それでは順に説明しましょう。

BOM

まずは、頭のゴミから。それは、 Unicode の BOM(Byte Order Mark)です。

Unicode の仕様で定められている物で、その文書の Byte order を識別するための特殊なしるしなのですが、これが前に付いていると "#!" が認識されません。

改行コード

そしてお尻のゴミ、それは、キャリッジリターン<CR>文字です。

Windows では、改行は <CR><LF> の2文字なのに、Linux では <LF> 単体で改行として扱うため、<CR>は行の末尾に付いた普通の文字として扱われてしまいます。

Windows のエディタの設定

これらの問題を避けるためには、Windows で使用しているエディタの設定を変更するのが素直でしょう。

PyScriptorの場合、メニューの「ツール」→「オプション」→「IDEオプション」で出る設定ダイアログの「Edtor」の項を以下のように設定します。

Default file encoding for new file sf_UTF8_NoBOM
Defaule line break format for new files sfUnix

このような設定で作られたファイルは、メモ帳などの低機能エディタでは編集できなくなるので、設定には好みが分かれるところでしょう。ファイルの共有がSubversionなどのSCM経由で行われるなら、SCMツールに改行コードの変換を任せるのも手です。

また、WindowsPython では "#!" を書く意味は無いのですが、ソースコードの頭には常に定番の2行

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

を何も考えずに打ち込んでおくことをおすすめします。

不味いファイルが出来てしまったら

前述の設定は、新規ファイルの作成時のデフォルト値なので、既に不味い状態のテキストファイルが出来てしまったら、それは別途修正する必要があります。

問題に気付くのは、いざ Linux 上で作業を開始した時でしょうから、Linux上で修正してしまうのがスマートでしょう。たとえば vim を使うなら以下のようにします。

一旦ファイルを vim で開く。

$ vim ファイル名

以下のとおりにキーを叩き、フォーマットを指定して保存。

:set nobomb fileformat=unix
:w

vimを終了。

:q

これで、適切なテキストフォーマットのファイルになっているはずです。

$ hexdump -C ファイル名 | head -n 3
00000000  23 21 2f 75 73 72 2f 62  69 6e 2f 65 6e 76 20 70  |#!/usr/bin/env p|
00000010  79 74 68 6f 6e 0a 23 20  2d 2a 2d 20 63 6f 64 69  |ython.# -*- codi|
00000020  6e 67 3a 20 55 54 46 2d  38 20 2d 2a 2d 0a 22 22  |ng: UTF-8 -*-.""|