読者です 読者をやめる 読者になる 読者になる

k-holyのPHPとか諸々メモ

Webで働くk-holyがPHP(スクリプト言語)とか諸々のことをメモしていきます。ソースコードはだいたいWindowsで動かしてます。

真性PHPerでも分かった?FabricでWindowsからファイルアップロード

PHP Python Fabric Windows

Windows7 (MinGW / MSYS) で Fabric の続きです。

ファイルは作業ディレクトリに上げてからサーバ上でPHPスクリプトを実行すれば思いのままに…!とか考えてましたが、色々やるうちにまどろっこしく感じてしまい、結局Python/Fabricで何とかすることにしました。

同じことで迷わないためのメモです。環境は Windows7 / MSYS 1.0 / Python 2.7 / Fabric 1.6 です。

ローカルにカレントディレクトリ以下のhogeディレクトリがあればサーバ上にも作成し、ディレクトリ内の拡張子phpのファイルをアップロードして所有者apacheで実行できるように

なんだかえらく状況を限定してますが、そういうことをしたかったのです。

ローカルでのファイル/ディレクトリのパス操作には os.path モジュールが利用できます。

Fabricでサーバのファイル/ディレクトリ操作には fabric.contrib.files モジュールが利用できます。

fabfile.py

from fabric.api import env, run, local, put
from fabric.contrib.files import exists
import os, glob

env.hosts = ['192.168.4.130']
env.port = 22
env.user = 'k_horii'
env.key_filename = '~/.ssh/id_rsa'
env.password = 'hoge'

def test_copy_dir_files():
    local_dir = '%s\\hoge' % (os.path.dirname(__file__))
    server_dir = '~/hoge'
    if os.path.exists(local_dir):
        if not exists(server_dir):
            run('mkdir -p %s' % (server_dir))
        put('%s\\*.php' % (local_dir), server_dir, use_sudo=True, mode=0755)
        run('sudo chown -R apache: %s' % (server_dir))

上記の変数 local_path ですが、Windowsなので os.path に使えるディレクトリの区切り文字は '\' になるため、あのような記述にしています。詳しくは後ほど。

Fabricの put() などでは適宜読み替えてくれるようですが、os.path ではそうはいきませんでした。~/hoge も解釈してくれません。

put() してから chown していますが、ローカルでも所有者や権限が適切に設定されていれば put(local_path, remote_path, mirror_local_mode=True) でいいかもしれません。

chown を sudo('chown') ではなく run('sudo chown') でやってるのは、sudo() だと ~/hoge が /root/hoge になってしまうためです。

put() のオプション引数に use_sudo と mode があるのは便利ですが user とか owner のようなオプションが、更に贅沢を言えばディレクトリがなければ再帰的に作ってくれるオプションがあれば非常に便利なのですが…。

なお、ディレクトリ内ファイルの取得は当初は glob モジュールを使ってみましたが、マニュアルをよく読めば put() でワイルドカードと設置先のディレクトリを指定できました。

参考 Operations — Fabric 1.6 documentation

実行結果はこうなります。

(oppy)[k_horii@horii ~]$ fab test_copy_dir_files
[192.168.4.130] Executing task 'test_copy_dir_files'
[192.168.4.130] run: mkdir -p ~/hoge
[192.168.4.130] put: c:\Users\k_horii\hoge\a.php -> /home/k_horii/hoge/a.php
[192.168.4.130] put: c:\Users\k_horii\hoge\b.php -> /home/k_horii/hoge/b.php
[192.168.4.130] put: c:\Users\k_horii\hoge\c.php -> /home/k_horii/hoge/c.php
[192.168.4.130] run: sudo chown -R apache: ~/hoge

Done.
Disconnecting from 192.168.4.130... done.

サーバ側で確認

[k_horii@cent6h01 ~]$ ls -lap ~/hoge
total 20
drwxrwxr-x. 2 apache  apache  4096 Apr 11 18:11 ./
drwx------. 7 k_horii k_horii 4096 Apr 11 18:11 ../
-rwxr-xr-x. 1 apache  apache     6 Apr 11 18:11 a.php
-rwxr-xr-x. 1 apache  apache     6 Apr 11 18:11 b.php
-rwxr-xr-x. 1 apache  apache     6 Apr 11 18:11 c.php

ちゃんと所有者と権限が設定されています。

真性PHPerでも分かる os.path

os.path モジュールとPHP関数の比較をしてみます。

PHP   : __FILE__
Python: __file__

PHP   : dirname()
Python: os.path.dirname()

PHP   : basename()
Python: os.path.basename()

PHP   : file_exists()
Python: os.path.exists()

PHP   : realpath()
Python: os.path.realpath()

PHP   : is_dir()
Python: os.path.isdir()

PHP   : is_file()
Python: os.path.isfile()

PHP   : filemtime()
Python: os.path.getmtime()

PHP   : filesize()
Python: os.path.getsize()

なんだか大丈夫そうな気がしてきませんか?

他にもPHPにはない os.path.normpath() や os.path.relpath() なんかもあります。

とりあえず、ローカルで事前に用意したファイルがあるかチェックしてサーバに設置するだけなら、この os.path モジュールを使って何とかできたということで。

Windowsのパス問題

前述の「os.path に使えるディレクトリの区切り文字は '\'」の問題について。

MSYSやFabricは ~/hoge といったローカルのパスを解釈してくれますが、os.path はそうはいきません。

たとえばMSYS環境でFabricを実行する場合はk_horiiのホームで fabric.api.local() を使って current_dir = local('pwd', capture=True) とすれば /c/Users/k_horii が返ります。

しかし、それを os.path.normpath() で変換すると \c\Users\k_horii に、os.path.realpath() で変換すると c:\c\Users\k_horii になり、結局いずれも os.path で扱えない無効なものになってしまいます。

ただ、ローカルでのファイル/ディレクトリの存在チェックやカレントディレクトリの参照が必要なければ、普通に fabric.api.put() だけで問題ないと思います。

それに、ファイルの設置先が接続中ユーザのホームディレクトリではなく固定のディレクトリであれば、もっと自然な形で書けそうです。

試しに書き換えてみました。

fabfile.py

from fabric.api import env, run, local, put
from fabric.contrib.files import exists

env.hosts = ['192.168.4.130']
env.port = 22
env.user = 'k_horii'
env.key_filename = '~/.ssh/id_rsa'
env.password = 'hoge'

def test_copy_dir_files():
    server_dir = '/tmp/hoge'
    if not exists(server_dir):
        run('mkdir -p %s' % (server_dir))
    put('~/hoge/*.php', server_dir, use_sudo=True, mode=0755)
    sudo('chown -R apache: %s' % (server_dir))

実行結果はこうなります。

(oppy)[k_horii@horii ~]$ fab test_copy_dir_files
[192.168.4.130] Executing task 'test_copy_dir_files'
[192.168.4.130] put: c:/Users/k_horii/hoge\a.php -> /tmp/hoge/a.php
[192.168.4.130] put: c:/Users/k_horii/hoge\b.php -> /tmp/hoge/b.php
[192.168.4.130] put: c:/Users/k_horii/hoge\c.php -> /tmp/hoge/c.php
[192.168.4.130] sudo: chown -R apache: /tmp/hoge

Done.
Disconnecting from 192.168.4.130... done.

put() のローカル側のパスがおかしなことになってますが、エラーにはなりませんでした。

PHPでもWindowsのディレクトリセパレータ対策は施されていますが、Fabricでも同様のようです。これは嬉しい。

サーバ側で確認

[k_horii@cent6h01 ~]$ ls -lap /tmp/hoge
total 20
drwxrwxr-x. 2 apache apache 4096 Apr 11 18:42 ./
drwxrwxrwt. 9 root   root   4096 Apr 11 18:42 ../
-rwxr-xr-x. 1 apache apache    6 Apr 11 18:42 a.php
-rwxr-xr-x. 1 apache apache    6 Apr 11 18:42 b.php
-rwxr-xr-x. 1 apache apache    6 Apr 11 18:42 c.php

ちゃんとファイルが設置され、所有者と権限も設定されました。結局、PHPer的なos.pathの説明は意味なかった…?

Fabricの機能もまだきちんと把握できていないので、もっと楽な方法があるかもしれません。引き続き勉強します…。