Colab Python から Google Drive を操作する
Colab の Python から Google Drive を使いたい: Python の中から shell 操作的なことをしたかったら、今までは os.system や os.spawn を使うのが定番でしたが、Document [1] を見たら、「そんなとき、これからは subprocess を使おう」と書いてあったので、以下ではこれに従うことにします。
使用する module は subprocess.run
です。
ただ、これだけだと Notebook に標準/エラー出力が表示されない(*1)ので、Colab の場合は subprocess.PIPE
を使って Notebook の画面に出力します(*2)。
さらに凝ったことをやりたい人のためには subprocess.Popen
も用意されていますが、Colab 上でそこまでやることもなさそうなので割愛。
(*1) たとえば、ls
でディレクトリ・リストを取っても「できました😃」と返事があるだけで、肝心の結果は表示されません。これでは意味がないので、subprocess.PIPE
で stdout
と stderr
を外に出し(binary)、decode()
でデコードしてして表示させています(もし文字化けするようでしたら、decode('foo')
の foo の部分を utf-8, sjis など適当に変えて試してみてください)。
(*2) 実は、Python 3.7 以上を使っているのなら、capture_output=True
を使った方が手っ取り早いのですが、Colab の Python は 本稿執筆時点で Python 3.6.9 (as of 2020-12-06) なので、ギリギリ使えません。
バージョンは shell=True 型と shell = False 型の 2 つ
バージョンは、ちょっと危険だけどいろいろできる版(shell=True 型)とちょっと機能制限はあるが そこはかとなく安全版(shell=False 型)の 2 タイプをご用意いたしました。
ちょっと危険だけどいろいろできる版
Script 1 を Colab のテンプレに貼り付けておけば、terminal からファイル操作するような感じで Drive を使えるので便利です。
shell=True 版では Command を、'ls -lh'
のように、quotation marks (single or double) の中に普通に書けば OK です。
Script 1 shell=True type
from subprocess import run, PIPE my_process = run( 'ls -lh', # コマンドはスペース区切りで渡す(terminal で操作する感じ)。 shell=True, # shell mode だと出来ることの幅が広がるが、その分セキュリティに気をつける必要。 cwd='/content/drive/MyDrive/', # Current directory 指定。適宜設定しておくと便利。省略可。 stdout=PIPE, stderr=PIPE # これを書いておかないと結果が返って来ないので寂しい。 ) print(my_process.stdout.decode(), my_process.stderr.decode())
そもそも subprocess
を使う時点で一定のリスクが生じるわけですが(Shell injection [2] など)、このバージョンでは Shell mode を True
にしてあるため大概の command が使えるメリットがある一方で、その分ちょっぴりリスクが増加します。
でも普段使いで自分だけ使う分にはそんなに心配しなくても良いかなぁと(*3)。
(*3) Closed な環境で Colab は自分しか使わない前提では問題になりませんが、 Colab で share する場合などを考えた場合、一定のリスクは意識しておいた方が良さそうです。
ちょっと機能制限はあるがそこはかとなく安全版
Script 2 は、若干の機能制限(wild card ‘*’ が使えないなど)があるけれど、セキュリティー的には若干リスクが低い shell=False
版です。
shell=True
版との大きな違いは、command の与え方です。
shell=False 版では Command を、['ls', '-lh']
のように要素にバラして list にして与えます。
Script 2 shell=False type
from subprocess import run, PIPE my_process = run( ['ls', '-lh'], # コマンドはスペース区切りではなく、リストで渡す。 shell=False, # Default が False なので省略可能ですが念の為(False の方が安全)。 cwd='/content/drive/MyDrive', # Current directory 指定。適宜設定しておくと便利。省略可。 stdout=PIPE, stderr=PIPE ) print(my_process.stdout.decode(), my_process.stderr.decode()) # 結果確認用
Enjoy!
References
[1] Python documentation. Subprocess management ( ja )
[2] IPA (2007). セキュア・プログラミング講座 コマンド注入攻撃対策
[1] Python documentation. Subprocess management ( ja )