Pythonista subprocess Pycharm

Python学習【365日チャレンジ!】100日目のマスターU(@Udemy11)です。

なんだかんだいって、100日継続することができてめっちゃ嬉しいマスターUです。

100って数字はやっぱり興奮しちゃいますよね!

とはいえ、まだ目標達成まであと265日あるので、しっかりと気を引き締めてPython学習を継続していきたいと思います。

それではさっそく今日もPython学習をはじめましょう!

昨日の復習

昨日は、tempファイルの取り扱いについて学習しました。

一時ファイルであるtemporaryファイルですが、【I/Oバッファ】に書き込まれるものと、実際にtemporaryファイルやフォルダに書き込まれて、作業が終了したら削除されるものがありました。

import tempfile

with tempfile.TemporaryFile(mode='w+') as temp:
    temp.write('What is your name?')
    temp.seek(0)
    print(temp.read())

with tempfile.NamedTemporaryFile() as n_temp:
    with open(n_temp.name, 'w+') as f:
        f.write('How are you?')
        f.seek(0)
        print(f.read())

with tempfile.TemporaryDirectory() as temp_d:
    print(temp_d)

出力結果

What is your name?
How are you?
/private/var/mobile/Containers/Data/Application/CCBA549E-E8FE-4616-A553-00250CF9575D/tmp/tmpqme27m38

3行目のTemporaryFileメソッドは、情報を【I/Oバッファ】に記録して削除します。

8行目のNamedTemporaryFileメソッドと14行目のNamedTemporaryFileは、tmpフォルダにファイルやフォルダを作成して、作業終了後に削除します。

NamedTemporaryFileには、引数deleteが用意されていて、デフォルトがTrueですが、Falseを指定すれば、作業終了後もファイルを削除せずに残すことができました。

フォルダも同様に、mkdtempメソッドを使えば、自動的に削除せずに残すことができますが、自分で作ったフォルダとその中身を削除する必要がありました。

詳しくは昨日の記事を参照してください。

それでは本日の学習をすすめましょう!

Pythonistaでsubprocess

subprocessモジュールは、Pythonファイルからコマンドを実行できる標準ライブラリです。

Macならターミナル、WindowsならコマンドプロンプトをPythonファイルから実行できて、少し前に学習したファイル操作のosモジュールの拡張版みたいな感じがします。

MacのターミナルとWindowsのコマンドはコードが違いますが、Appleが大好きなので、Macで使うターミナル用のコマンドですすめていきます。

いつものように、Pythonistaでコードを書いているときにこんな考えが浮かんできました。

マスターU

iPhoneってターミナル使えないから、subprocessって、Pythonistaだと使えないんじゃないの?

といっても実行してみないことにはわからないので、とりあえずPythonistaで下記のコードを実行してみました。

import subprocess

subprocess.run(['ls'])

結果はやはり、エラーが返されちゃうんですよね。

パーミッションエラーとのことなので、読み込みが禁止されているみたいですが、StaShでlsコマンドを使えば表示されるんです。

Pythonista StaSh subprocess

Pythonistaではsubprocess.run()が使えないようなので、別の方法でsubprocessメソッドを実行してみます。

Pycharmでsubprocess

Pythonista subprocess Pycharm

Pythonistaではsubprocessメソッドが実行できないことがわかったので、Pycharmでsubprocess.run()を実行してみます。

import subprocess

subprocess.run(['ls'])

出力結果

Keyword
box
design
lesson.py
lesson_package
session6
test.tar.gz
test.txt
test.zip

subprocessモジュールは、run関数を使って、リストでコマンドを指定すれば、ターミナルでコマンドを実行した場合と同じ結果が表示されます。

Pythonista Pycharm subprocess

lsコマンドは、現在の階層にあるファイルやフォルダを表示してくれるコマンドです。

ターミナルの場合は、半角スペースに続けてオプションを指定しますが、subprocess.run()の場合は、リストの'ls'のあと、,(カンマ)+半角スペースに続けてオプションを指定します。

import subprocess

subprocess.run(['ls', '-al'])

出力結果

total 32
drwxr-xr-x  13 bigmacpro  staff  416 May  6 18:04 .
drwxr-xr-x   4 bigmacpro  staff  128 Feb 16 20:23 ..
drwxr-xr-x   8 bigmacpro  staff  256 May  6 18:03 .idea
drwxr-xr-x   4 bigmacpro  staff  128 Apr 26 14:24 Keyword
drwxr-xr-x   5 bigmacpro  staff  160 Apr 11 00:08 box
drwxr-xr-x   3 bigmacpro  staff   96 Apr 29 21:33 design
-rw-r--r--   1 bigmacpro  staff   49 May  6 18:04 lesson.py
drwxr-xr-x   8 bigmacpro  staff  256 Apr  4 16:27 lesson_package
drwxr-xr-x   8 bigmacpro  staff  256 Apr 29 21:34 session6
-rw-r--r--   1 bigmacpro  staff  220 May  3 20:36 test.tar.gz
-rw-r--r--   1 bigmacpro  staff    9 May  6 17:57 test.txt
-rw-r--r--   1 bigmacpro  staff  488 May  3 21:34 test.zip

-alは、ファイルの詳細を表示するオプションです。

表示される内容は、

  • ファイルの種類とパーミッション(許可属性)
  • ハードリンク数
  • 所有者
  • グループ名
  • ファイルサイズ
  • タイムスタンプ
  • ファイル名

となっていて、こういうコマンドを使って何をするのかは全く想像がつかないのですが、ちょっとだけプログラマー気分になれちゃうんですよね。

非推奨のos.system

古いPythonでは、os.system()を使ってコマンドを実行していましたが、Python3.5からsubprocess<メソッドにrun()関数が追加され、コマンドを扱えるようになりました。

今でもos.system()は使用可能ですが、非推奨となっています。

一応、使い方を紹介しておきます。

import os

os.system('ls')

出力結果

Keyword
box
design
lesson.py
lesson_package
session6
test.tar.gz
test.txt
test.zip

リストではなく、直接引数に'ls'を指定することでコマンドを実行できます。

あくまで参考ということで、古いプログラミングのコードにはos.system()を使っているものもあると思うので、見かけたらわかる程度に憶えておきましょう。

shell=Trueは使わない

以下の内容は、参考として憶えて置く程度でいいのですが、subprocessメソッドは、ターミナルのコマンドをそのまま扱えるようにできます。

引数shellがデフォルトでは、Falseになっているのですが、shell=Trueとすれば、リストにして,(カンマ)で区切らなくてもターミナルのコマンドをそのまま実行することができます。

import subprocess

subprocess.run('ls -al | grep test', shell=True)

shell=Trueにすることで、シェルに処理をさせることができ、このコマンドの場合は、フォルダ内のファイルやフォルダの中からtestが含まれたものを検索するという処理をシェルにさせています。

os.system()の場合は、引数を指定しなくてもデフォルトで同じことができますが、セキュリティーの関係で、この使い方はおすすめできないということです。

コマンドのことはきちんと理解していませんが、シェルに処理をさせると|(パイプ)を付け加えて追加の処理をさせることができるため、意図しない処理や悪意を持ってディレクトリを丸ごと削除してしまうこともできるらしいです。

例えば、shell=Trueにしたsubprocess.run()関数に,
コマンドを入れた変数を代入する形でコマンドを実行する場合、何かのきっかけで| rm -rf *(書き込み許可がなくてもディレクトリ以下のすべてのファイルやフォルダを削除するというコマンド)を追加してしまうと、ディレクトリがすべて削除されて大変なことになってしまいます。

cmd = 'コマンド | rm -rf *'
subprocess.run(cmd, shell=True)

どんなときに、こんな問題が起こるのかはわかりませんが、このようなリスクがあるということで、Pythonファイルからのコマンド実行は、os.systemからsubprocess.run()に変更されたのだと思います。

すべてを理解できない

100日継続してPythonを学習してきましたので、ある程度はPythonのことがわかってきたつもりですが、まだまだ5%もわかっていないような感じがします。

何気なくPython公式ドキュメントをのぞいてみると、知らないことだらけで、ほんとにほんの一部だけしか学習できていない現実を思い知らされてしまいます。

ほんと、Pythonを開発した人の頭の中ってどうなってるんでしょうね?

とはいえ、できることといえば、一つ一つわからないことを実践しつつ、身につけていくしかないので、これからも小さな積み重ねをコツコツと積み上げていこうと思います。

それでは明日もGood Python!