Contents
- 1 cronで実行できないときのチェック項目
- 2 パーミッションについて3
- 3 パーミッションについて2
- 4 SSH
- 5 cron実行するpythonプログラムでprintするのはだめ
- 6 cronでpythonプログラムを実行するにはshファイルをかませる
- 7 cron実行時のカレントディレクトリ
- 8 ロリポップのpythonはバージョン3.4.1
- 9 レンタルサーバー上で標準で含まれていないライブラリ(モジュール)を使ったプログラムを動かすには
- 10 レンタルサーバー上で標準で含まれていないライブラリ(モジュール)を使ったプログラムを動かすには ver.2
- 11 パーミッション
- 12 レンタルサーバー上でPHPからPythonを呼ぶ
cronで実行できないときのチェック項目
個人的にやりがちな項目。
- パーミッションをチェックする。.phpの場合は所有者に読み取り権限があるか?.shの場合は所有者に読み取り権限と実行権限があるか?
- .pyの場合は標準出力(print)や標準エラー出力をしてメール送信に失敗し中断されていないか?そうであればsys.stdout = Noneとする。ちなみに.phpの場合、標準出力のechoやprintをしても.pyのような問題は起こらないので手間をかけないですむ。
- .pyの場合、実行ファイルの場所をカレントディレクトリに設定しているか?cron実行時のカレントディレクトリはユーザーのホームディレクトリなので、os.chdir(os.path.dirname(os.path.abspath(__file__)))で設定する必要がある。ちなみに.phpの場合は実行ファイルの場所がカレントディレクトリになっているので.pyのような手間をかけないですむ。
- cronで「/bin/sh: /home/users/2/main.jp-return/web/〇〇〇.sh: /bin/bash^M: 誤ったインタプリタです: そのようなファイルやディレクトリはありません」というエラーが出る場合。Windowsでファイルを作成したため、.shファイルにまずい文字コード(CRLF)がまじっているのが原因。ファイルをVSCodeで開いて右下のCRLF→LFをクリックするとまずい改行コードを削除できる。この症状のやっかいなところは、CRLFが混じっていてもSSHのコマンドラインから実行するとできてしまうところ。cronで実行するときしか再現しない。まずい文字コード(CR=^M)が混じっているかどうかは「cat -e 〇〇〇.sh」で確認することができる。
まとめると、パーミッションはPHP、Python両方で注意が必要。標準出力とカレントディレクトリの問題はPythonでのみ注意が必要。
なんでPythonだけこんな問題が起こるのだろう。.shをかましていることが関係しているのかな?
パーミッションについて3
パーミッションをいじる実験のメモ。
一般ユーザーのブラウザからリクエストをもらいApacheが実行する場合
- .htmlや.txt, .datなどの単なるファイル→その他に読み取り権限を与える。
- .shファイルなどのみせたくないファイルは、その他に読み取り権限を与えなければ隠すことができる。
- .phpファイル→所有者に読み取り権限を与える。
cronで実行する場合
- .phpファイル→所有者に読み取り権限を与える。
- .shファイル→所有者に読み取り権限と実行権限を与える。読み取り権限だけではだめだし、実行権限のみでもだめ。なんでだろ。
以上より、基本的な方針
- グループにはなんの権限も与えなくていい。
- 見せたいファイルはその他に読み取り権限を与えるが、見せたくないファイルはその他に読み取り権限を与えない。
- 実行させたいPHPファイルは所有者に読み取り権限を与える。実行させたくないPHPファイルはサブディレクトリに放り込んで、サブディレクトリの実行権限を与えない。実行するには所有者とその他の両方の実行権限が必要なようなので、どちらか一方のチェックを外せばいいのだが、心配なら両方はずすべし。
- cronで実行するファイル(.phpや.sh)は所有者にのみ権限を与え、グループ・その他にはなんの権限も与えない。
パーミッションについて2
ファイルの種類によってちがう。txtファイルなどの単なるファイルと、phpやpyなどのスクリプトファイル。shファイルは単なるファイル側。
単なるファイルはその他に読みとり権限があればダウンロードできる。スクリプトファイルはダウンロードできない。
スクリプトファイルはその他に読みとり権限や実行権限を与えても意味はない。スクリプトファイルはダウンロードができない。
スクリプトファイルは所有者に読みとり権限(実行権限ではない)があればPHPで処理された結果(HTMLファイル)が表示される(ダウンロードされる)が、所有者に読みとり権限がなければ「Access denied.」と表示される。
クライアントがPHPファイルを要求するとApache(所有者)がPHPを実行し、PHPがPHPファイルを読み込むのだと思う。だから読込権限が必要であり、実行権限は要らない。
一般的な設定では、Apacheのプログラムを実行しているユーザーは、apacheという名前の普通のユーザーでして、これはその他のユーザーのカテゴリに属します。 ですので、htmlファイルなどのWEBに公開するファイルのパーミッションは、その他のユーザーに読み込み権限を与えればいい、ということになります。
PHPファイルはプログラムなどで、実行権限が必要と考えてしまいますが、Apacheのデフォルトの設定では読み込み権限があれば動作します。 Apacheのデフォルトの設定では、PHPはモジュールモードとして動作していて、PHPのソースコードをApacheが読み込み、PHPモジュールとして実行するので、 読み込み権限のみ与えておけば大丈夫です。 ログファイルなどのプログラムから書き込まれる必要があるファイルには、書き込み権限を与える必要があります。
https://realid-inc.com/column/2013/04/05-190943
以上から、PHPファイルにはその他に読込権限を与えれば動作することになるが、ロリポップには「Access denied.」となる。ロリポップではApacheのプログラムを実行しているユーザーは、apacheという名前の普通のユーザーではなく所有者になっているようだ。
しかしそうなると所有者に権限を与えずその他に読込権限を与えておけば単なるファイルをダウンロードできることの説明がつかない。いったいどういうことなんだろう。
Xドメインサーバー無料版の場合、PHPはPHPサーバーにアップロードするようになっており、HTML用のサーバと切り分けがされています。
~中略~
PHPサーバーのApacheの権限は「その他」ではなく「ユーザー」になっていると思われます。
https://itstudio.co/2015/08/20/4616/
なるほど、ロリポップもXドメインサーバー無料版とおなじなんだろう。単なるファイル(.txtや.html)をリクエストしたときとPHPファイルをリクエストしたときで異なるサーバーが動いているわけか。HTMLのサーバーはその他でPHPのサーバーは所有者が動かしていると。
サーバーを分けている理由はたぶん、PHPなどのプログラムから参照したいけどクライアントには見られたくないファイルのパーミッションを可能にするため。分けられていれば所有者に読込権限を与えてその他に与えなければいい。しかしサーバーを分けていないと不可能になる。
グループには基本権限を与える必要はない。与えないほうがいいと思う。
SSH
しばらく使ってないとあっさり忘れるのでメモ。
1 |
ssh ユーザー名@ドメイン名 -p ポート番号 |
cron実行するpythonプログラムでprintするのはだめ
cron実行するpythonプログラムでprintすると、例外を吐くこともなく停止する(2、3時間くらいはまった)。こちらによると、標準出力があるとメール送信しようとして失敗するのが原因。
print関数をぜんぶ排除するのは面倒なので、強引に標準出力しないようにしたらうまくいった。
1 2 |
import sys sys.stdout = None |
しかしググってもこのやり方はひとつも出てこないのでかなり強引なやり方なのだと思う。なんか弊害があるかも?
crontabを直接書ければいいのだけど、ロリポップではそれはできないようだ。また、shファイルでpythonを呼ぶ行の末尾に「> /dev/null 2>&1」を加えてもだめだった。強引だけど上記の方法しか思いつかない。
cronでpythonプログラムを実行するにはshファイルをかませる
ロリポップの場合、PHPプログラムはcron設定すると実行されるが、pythonプログラムは実行されない。以下のようなshプログラムをかませる必要がある。
1 2 |
#!/bin/bash /usr/local/bin/python /home/users/・・・/test.py |
このshファイルをcron設定すればOK。cronに実行権限を与えるため、パーミッションを700にするのを忘れないように。
こちらによると、cronで実行するときはpython2系が使われているらしい。3系にしかない機能を使うことでエラーとなり実行されていないように見えるのかな、と思ったが、以下のようなシンプルなプログラムをcron実行させてもtest.txtは生成されなかった。
1 2 3 4 5 6 |
import os os.chdir(os.path.dirname(os.path.abspath(__file__))) f = open('test.txt', 'w', encoding='utf-8') f.write(str(3 / 2)) f.close() |
3/2は3系では1.5となり2系では1となるはずで、それを確認するためのプログラムだったのだが、ファイルが生成すらされなかった。ちなみにwith構文でファイルオープンしていないのは、ひょっとしてwith構文って3系からかも、と疑ってのこと。
以上より、cronからpythonファイルを直接実行することはできないので、shファイルを作って間接的に実行するしかない(と思う)。
cron実行時のカレントディレクトリ
cron実行時のカレントディレクトリは実行ファイルのある場所ではなく、ユーザーのホームディレクトリ。
ファイルを生成したり参照する場合、実行ファイルのある場所にカレントディレクトリを移したい。Pythonの場合は以下。
1 2 3 4 |
import os # os.chdir(os.path.dirname(__file__)) # これでもcron実行できるが、テスト時にSSHでコマンドラインから実行すると__file__に相対パスが入ってきてエラーになるので↓のほうがいい。 os.chdir(os.path.dirname(os.path.abspath(__file__))) |
ロリポップのpythonはバージョン3.4.1
なので、3.4.1より後に追加された機能は使えない。
1 2 3 4 5 6 7 |
num = 3 # 3.6からなのでエラー s = f'{num}' # これも3.6からなのでエラー n = random.choices([1, 2, 3], k=2, weights=[0.2, 0.2, 0.6]) |
レンタルサーバー上で標準で含まれていないライブラリ(モジュール)を使ったプログラムを動かすには
こちらのページを参考にすればOK。たとえばBeautifulSoupを使ったプログラムをレンタルサーバー上で動かしたい場合。
vendorディレクトリをつくって、そのなかにお望みのモジュールを導入する。
1 2 |
mkdir vendor pip install beautifulsoup4 --target ./vendor |
pythonプログラムの冒頭に次を記載する。
1 2 3 4 5 6 7 |
import os import sys # vendor以下をスクリプトの読み込みパスに追加 sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'vendorのパス')) # あるいは絶対パスで指定する sys.path.append('vendorの絶対パス') |
あとはvendorをFTPでうpすると、BeautifulSoupを使ったプログラムを動作させることができました。感動した。
レンタルサーバー上で標準で含まれていないライブラリ(モジュール)を使ったプログラムを動かすには ver.2
ふと思いついて、わざわざローカルでモジュール導入してレンタルサーバーにUPしなくても、レンタルサーバー(ちなみにロリポップ)上でこれ
1 2 |
mkdir vendor pip install beautifulsoup4 --target ./vendor |
やっちゃえばいいんじゃね?とおもってやってみたらできた。ただしpipにパスが通っていなかったので、以下のように絶対パスで指定。
1 2 |
mkdir vendor /usr/local/python/3.4/bin/pip install beautifulsoup4 --target ./vendor |
pythonにパスが通っているので、こちらのほうがかんたんか。
1 2 |
mkdir vendor python -m pip install beautifulsoup4 --target ./vendor |
これは便利。このノリでSeleniumも動かせたら神なんだけどなぁ。
→たぶん無理だなー。ライブラリはpipで入るけど、Chromeがインストールできない。たぶんレンタルサーバーは独自のアプリをインストールできない。すでに用意されたものしか(pythonとかphpとか)実行できないんだと推察。VPSというのを契約すればできそうな感じ。
レンタルサーバー上で直接pip installすると、権限の問題とかpythonのバージョンが古かったりで失敗することがある。その場合はやはり↑のように、ローカルでpip installしてからFTPでUPするとうまくいくことがある。requests_oauthlibがそうだった。
パーミッション
FTPソフトでパーミッションとかいじりつついろいろ試してみたことのメモ。
ブラウザにディレクトリのアドレスを打つとかならず
1 2 3 |
403 Error 現在、このページへのアクセスは禁止されています。 サイト管理者の方はページの権限設定等が適切かご確認ください。 |
↑のページに飛ばされる。権限フル(パーミッション777)で与えていても。
自分でつくったディレクトリは例外なく↑のページに飛ばされるけど、wordpressにもとからあるディレクトリは例外があるみたい。wp-adminはログインページに飛ぶし(自分以外のwordpressを使ったブログでもおなじ)、wp-contentはなにも表示されない(403 Errorではなく真っ白なページ。ソースも空)。ほかのブログでは403に飛ばされた。ロリポ独自の仕様?よくわからん。もしかして俺のセキュリティ甘い?
ファイルはパーミッション004で表示できる。つまり「その他」に読み込み権限が与えられていえれば表示できる。サーバー(Apache?)が「その他」で実行されている(たぶん)。
→サーバーはChromeのデベロッパーツールのNetworkタブで確認できる。やっぱりApache。
→サーバーの仕様のページを発見。
ファイルは親ディレクトリの「その他」に実行権限が与えられていないと表示できない。サーバーが親ディレクトリを開けないから。
見られたくないファイルは親ディレクトリの「その他」に実行権限を与えなければいい。しかしcronとかで実行しているプログラムはどのユーザーで実行しているんだろう。後日調べる。
レンタルサーバー上でPHPからPythonを呼ぶ
コマンドラインからphpを実行する場合
PHPファイルに以下のように記載するとpythonプログラムを呼べる。
1 2 |
$command = "python sample.py" . " " . $var; exec($command, $output); |
pythonファイルでは以下のように書く。
1 2 3 4 |
import sys var = sys.argv[1] #PHPから変数$varを受け取る var += 1 #適当に処理して print(var) #PHPに変数を返す |
PHPファイルで以下のようにするとpythonの出力varを取得できる。
1 |
$rate = $output[0]; |
pythonで2回printすると2回目の出力は$output[1]に入っています。
cronでPHPを実行する場合
このパターンでかなりの時間はまったのですが、結論からいうと、することはひとつ。
- pythonコマンドを絶対パスで指定する。
つまり、以下のようにすればいい。
1 2 |
$command = "/usr/local/bin/python sample.py" . " " . $var; exec($command, $output); |
絶対パスで指定しないと、output[0]が空のままなのです。pythonファイル内でログファイルを吐こうとしてみてもファイルが生成されないので、どうやらpythonが実行されていないみたいなのですが原因がわかりませんでした。
さんざんググったのですが、どうやらコマンドラインから実行する場合とcronで実行する場合では実行ユーザーがちがうらしいのです。コマンドラインから実行するときはpythonコマンドへのPATHが通っているのに対して、cronで実行するときはPATHが通っていなかったのが原因でした。
というわけで、結論。pythonコマンド(pythonファイルではない)を絶対パスで指定すればいい。あるいは環境変数を設定する方法もあるようですが、そちらは詳しくは調べていません。
ちなみにcronでPHPを実行するとき、カレントディレクトリはPHPファイルがあるディレクトリになっているみたい。だからpythonファイルは絶対パスじゃなくていい(冒頭でchdir関数呼ぶ必要なし)。