python3が入っていない環境でも、python3を入れてから実行できるpythonファイルを作っていた。
目論見
- python書いてみたい
- どうせ書くならpython3で書きたい
- どうせ書くなら他人にもつかってもらいたい
- 社内環境の標準pythonは2.4(結構辛い!)(誰も使ってない!)
- 社内の人に「python3に上げて使ってネ!」と言っても100%無理
- python3に上げる用のsh書いてもファイルが2つになって余計に心理的ハードルが上がる(と思う)
- じゃあ実行するときに自動アップデートかけてバージョンを意識させなければいいじゃない(ファイルも1つになるじゃない)
開発環境を誰でもrootでなんでもできるってあまりなさそうなことが前提ですが。
参考
Perl, Python 及び Ruby スクリプトにおける正しいshebangの書き方
shebang複数書けばいけるんじゃね!?と思い探しました。
まさにドンピシャで、この記事がなければこんな馬鹿なことはしませんでした。
PHPとしても実行できるRubyの書きかた
趣旨は違いますが、1つのファイルで複数の言語、ということでインスピレーションを受けました。こっちもやってみたい。
コード
#!/bin/sh
# -*- coding: UTF-8 -*-
""":"
# ここにshを書く
# インストールバージョンと圧縮ファイルの設置&展開
tmp='/tmp/'
pythonver='3.4.3'
binarypath=$tmp'Python-'$pythonver
# インストール先
lib='/usr/lib/'
srcpath=$lib'python'$pythonver
# python3のシンボリックリンク設置場所
bin='/usr/bin/'
lnname='python3'
if [ -f $bin$lnname ]; then
# python3があればそのままpython実行
echo 'インストール不要'
exec $lnname "$0" ${1+"$@"}
fi
echo 'インストール必要'
if [ -f $binarypath'.tgz' ]; then
echo $binarypath'.tgz は既にあります'
else
echo $binarypath'.tgz をダウンロードします'
wget https://www.python.org/ftp/python/${pythonver}/Python-${pythonver}.tgz -P ${tmp}
fi
if [ -d $binarypath ]; then
echo $binarypath' は既にあります'
else
echo $binarypath' に展開します'
tar zxf $binarypath'.tgz' -C $tmp
fi
if [ -d $srcpath ]; then
echo $srcpath' へインストール済みです'
else
echo $srcpath' へインストールします'
# cd すると後のpython実行でコケるので、インストール後に戻る
pwd=`pwd`
cd $binarypath && ./configure --prefix=${srcpath} && make && make install
cd $pwd
fi
if [ -f $bin$lnname ]; then
echo $bin$lnname' 設置済みです'
else
echo $bin$lnname' を設置します'
ln -s $srcpath'/bin/python3' $bin$lnname
fi
exec $lnname "$0" ${1+"$@"}
"""
# ここからpython
import sys
version = sys.version_info
if version[0] == 2:
print('2')
else:
print(version[0])
最初はpython
のバージョンが2ならpython
を3に置き換えていたのですが、他に影響が出そうで、事実yumが動かなくなったりしたのでやめました。
下の失敗例はその名残ですね。
失敗例
動的言語ってインタプリタで一行ずつ解析して実行すんだろ?
じゃあ問題がある箇所を通過しなければOKだな?
普通に使っているときはそんなこと無いと常々痛感するのですが、一度目的が出来ると自分の都合がいい方に思い込みます。
python内でバージョン判定し、python内でpythonを更新しようとしました
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
import os
import tarfile
version = sys.version_info
if version[0] == 2:
print 'バージョン2のため、3に更新します'
if not os.path.isfile('/tmp/Python-3.4.3.tgz'):
print 'ダウンロードします'
os.system('wget https://www.python.org/ftp/python/3.4.3/Python-3.4.3.tgz -P /tmp')
else:
print '/tmp/Python-3.4.3.tgz はすでに存在しています'
# 2.4なのでextractallはナイよー
if not os.path.isdir('/tmp/Python-3.4.3'):
print '/tmp/Python-3.4.3 に展開し、インストールします'
tf = tarfile.open('/tmp/Python-3.4.3.tgz', 'r')
tf.list()
for item in tf:
tf.extract(item, '/tmp/')
# ビルド
if not os.path.isdir('/usr/lib/python3.4.3'):
os.system('cd /tmp/Python-3.4.3/ && ./configure --prefix=/usr/lib/python3.4.3 && make && make install')
else:
print '/usr/lib/python3.4.3 にインストール済みです'
else:
print '/tmp/Python-3.4.3 に展開済みです'
# /usr/bin/python の置き換え
if not os.path.isfile('/usr/bin/python.old'):
os.rename('/usr/bin/python', '/usr/bin/python.old')
os.symlink('/usr/lib/python3.4.3/bin/python3.4', '/usr/bin/python')
# 既存の/usr/bin/python2 がpython へのシンボリックリンクなので更新しておく
# 上書きはムリなので一回削除する
os.remove('/usr/bin/python2')
os.symlink('/usr/bin/python2.4', '/usr/bin/python2')
else:
print '/usr/lib/python は置き換え済みです'
sys.exit()
# ここは3の場合に実行されるはず
print('test')
python2の時点でprint('test')
がセーフだったので「いける!」と思ったのですが、3に上げた時点でやっぱりprint
でsyntaxerror
調べると、python2ではprint
、print()
どちらも使えるようで…
思い込みに拍車をかけられました(責任転嫁)
まぁ、書き始める前にちょうどPHPの実行時とコンパイル時を読んでまさに無理だと悟っていたのですが。
php hoge.phpのようにコマンドにソースコードのファイルを指定して直接実行してるように見えたとしても、現代的なインタプリタはソースコードをパースしたあと仮想機械のバイトコードにコンパイルする。RubyもPythonもPerl5もEmacsも同様に。
**でも、python2側でprint
使わず、syntax
を守ったpython3ならこれでも動くかも!?**と今思いました。
使用予定は無いです。