pythonカスタム関数§05:CreateBatfile() Pythonファイルを実行できるバッチファイルを自動生成

Pythonをもっと便利にしよう!

カスタム関数シリーズでは、便利に使えるオリジナル関数を紹介しています。

※カスタム関数は、一般的には「ユーザー定義関数」と呼ばれています

Pythonファイルの編集をエディタで編集する人必見

pythonのプログラムを編集するには、いろいろな方法がありますよね

  1. 【IDLE】標準でついてくる開発環境IDLEで編集して、実行させる
  2. 【Spyder】Anacondaで環境構築した人はこのSpyderで編集して、実行させる
  3. 【VScode】マイクロソフトのVisual Studio Codeで編集して、実行させる
  4. 【エディタ】自分の気に入ったエディタで編集 → ファイルをダブルクリック実行

上記の4つのうち1~3(IDLE,Spyder,VScode)のようなプログラム開発環境を使っている人は、たぶんこの記事の内容の役立つのは限定的かもしれません。

この記事は、エディタを使っている人にとっては、有用だと思いますので、最後まで見てください。

エディタで編集したあとの実行方法

気に入ったエディタでpythonプログラムを開発することは、ある意味マニアックかもしれません。

なぜなら、開発環境の揃ったVScodeなど使うと、編集しているときもエラーチェックや補完機能など開発行為を効率向上させる仕掛けがいっぱいあるからです。

しかし!

エディタが好きで好きで仕方がない私のようなタイプは、いつでもエディタを触ったいたいのです。わたしは、エディタに「サクラエディタ」を使っています。かれこれ、10年くらいになります。無料だし、軽量だし、使っている人たちが育てている!まるでpythonのようではないですか・・・

サクラエディタについては、アメブロで紹介しました。以下リンクです。

Python §47 : 開発環境をエディタで行う(サクラエディタのセッティング)

 

Python §48 : 開発環境をエディターで行う(サクラエディターのマクロを使って実行)

 

このようなテキストエディタを使った場合にpythonのスクリプトを実行するには、いくつかの方法があります。

  1. ファイルエクスプローラーでスクリプトファイルをダブルクリック
  2. コマンドプロンプトから python myscript.py などと打ち込む
  3. バッチファイルを作って、1.と同じくファイル名をダブルクリック

ということが考えられます。

1.のファイルエクスプローラーでファイル名ダブルクリックして実行する場合は、もちろんpythonスクリプトとpython.exeの関連付けが必要です。これは、らくちんですが、実は課題があります。

実行したあとの表示がすぐに消えてしまいます。これは、ひとつ対処方法があります。

最後の行に a=input() といれておきます。aは適当な変数です。input()関数は入力関数で、Enterキーが押されるまで、待ってくれます。

このinput()関数作戦でも表示が消えることを防止できますが、まだ課題があります。

「バグがあった場合」です!

バグがあるとinput()関数が登場するまでにウインドウを閉じてしまいます。これでは、バグがどこで出ているかという情報すらも見ることができません。

次に2.のコマンドプロンプトからの実行ですが、結構面倒です。

まず、スクリプトファイルのあるフォルダに移動することが面倒くさい。Windowsのコマンドプロンプトを出したあとに階層を下って、スクリプトファイルにたどり着くのは至難の業。

ただし、この問題はちょっとしたTipsテクニックがあります。

ディクトリーを簡単に移動できるTipsテクニック

  1. ファイルエクスプローラーでスクリプトファイルのあるフォルダに移動
  2. フォルダが表示されているボックスの空いているところに「cmd」と入力してEnterキー

これで、フォルダの移動とコマンドプロンプトを楽に行えます。でも、このあとのファイル名の入力作業など、面倒なのは変わりないですね。

それでは、今回のメインの内容、バッチファイルの登場です。

バッチファイルでpythonを実行させる

さきほどのコマンドプロンプトでpython スクリプト名.py で実行できることを紹介しました。

これをそのままバッチファイルを作ってしまえば、フォルダ移動コマンドプロンプトの表示コマンドとファイル名の入力 などの作業をダブルクリックひとつで済ませることができます。

スクリプトファイルが myscript.py 、バッチファイルを myscript.bat と仮定しましょう。

バッチファイルに次のように入力します。

python myscript.py

このバッチファイル、もちろんpythonファイルを実行できますが、ウインドウが消えてしまいます。

そこで、バッチファイルで一時停止する方法が3つあります。

  1. PAUSE
  2. CMD
  3. TIMEOUT 60
python myscript.py
PAUSE

このバッチファイルでは、PAUSEのところでキー入力待ち状態になりますので、表示をとどめておくことができます。

 

python myscript.py
CMD

このようにCMDを入れておくと、コマンド実行待ちでやはり表示をとどめておくことができます。

コマンド待ちなので、抜けるには「EXIT」と入力すると終了できます。

python myscript.py
TIMEOUT 60

この場合のTIMEOUTは、タイマー待ちで、そのつぎの数字が秒数です。
この例では、60秒待ったあと終了しますが、何かキーを押すとタイマーを待たずに終了できます。

通常は、PAUSE を使うのでよいと思います。

バッチファイルを自動生成させる

せっかくバッチファイルで実行と一時停止できるようになったので、バッチファイルをスクリプトファイルから自動生成できるようにできれば、さらにプログラム作成と確認の作業が楽になりますね。

次にバッチファイルを自動生成するカスタム関数を示します。

def CreateBatfile(wfilename='run.bat',StrData=[]):
    #起動バッチファイルの生成
    dq='"'  #ダブルクォーテーション
    wfile=open(wfilename,'w')
    count=0
    wfile.write('cd '+dq+str(ScriptPath()[2])+dq+'\n')
    wfile.write('echo off'+'\n')
    wfile.write('python '+dq+str(sys.argv[0])+dq+' '.join(StrData)+'\n')
    writepath=WriteFilePath(Pathname='ReadWrite_Data') #出力場所を指定(パスのみ)
    
    s1="rem EXPLORER "
    s2=writepath
    s=s1+dq+s2+dq
    wfile.write(s+' \n')
    wfile.write('pause \n')
    wfile.flush()
    wfile.close

ひとつずつ説明していきましょう。

def CreateBatfile(wfilename='run.bat',StrData=[])

まず引数ですが、wfilenameは、バッチファイル名です。省略するとrun.batというファイル名が採用されます。その次のStrDataはリストで渡します。このStrDataはバッチファイルの引数になるので、pythonスクリプト側では、sys.argv変数に入ることになります。→こちらで少し触れています

sys.argv[0]がスクリプトファイルパスが入るので、今回のStrData[0]→sys.argv[1]という順で格納されます。あとで、実例でやることにしましょう。

dq='"'  #ダブルクォーテーション

これは、ダブルクォーテーションを入力しているコードを見やすくするために変数dqに入れています。

wfile=open(wfilename,'w')

書き出すためのバッチファイル名でファイルを書き込みモードでオープンしています。

    wfile.write('cd '+dq+str(ScriptPath()[2])+dq+'\n')
    wfile.write('echo off'+'\n')

ここは、一工夫してます。このバッチファイルがスクリプトファイルと異なる場所でもうまく動くようにスクリプトのフォルダをScriptPath()というカスタム関数で取得しています。

echo offは、バッチファイルでのコマンドを表示をオフしています。表示を確認したい場合は省きます。

wfile.write('python '+dq+str(sys.argv[0])+dq+' '+' '.join(StrData)+'\n')

この行で、pythonファイルを実行するコマンドを書き出しています。dqで挟んでいるのは、パス名にスペースなどが入っている場合も確実に実行できるようにしています。

joinにより、StrDataを最後に付加しています。pythonでオプションを使わない場合は不要です。

writepath=WriteFilePath(Pathname='ReadWrite_Data') #出力場所を指定(パスのみ)

このWriteFilePath関数は、カスタム関数(ユーザー定義関数)で、特定のフォルダを作成します。この関数では、もしフォルダが存在しない場合は作成してくれます。なお、バッチファイルを書き込む場所は、前述したコードの中にある変数wfilenameで決まります。もし、パスが書かれていない場合は、スクリプトパスに書き込まれます。

dバッチファイルの中身後半部分(ExplorerとPAUSE)

    s1="rem EXPLORER "
    s2=writepath
    s=s1+dq+s2+dq
    wfile.write(s+' \n')
    wfile.write('pause \n')

バッチファイルの後半の中身を出力しているコードです。ちょっとおまけ的な意味合いもありますが、ファイルエクスプローラーを起動する文面を加えています。

ただし、REM文となっているので、実際に有効にするには、バッチファイルからREMの文字を消して使ってください。このコードでは、EXPLORERにパスを渡して、pythonを実行したあとにファイルエクスプローラーが自動的に開くようにしています。通常はREMにて無効にしておいて、必要い応じて有効にするという使い方です。

そして、最後に肝心のpause文が入っています。これによって、バッチファイルをここで一旦停止させることができます。エラーの解析や表示をゆっくり見るときのための工夫がこのpauseです。

CreateBatfile() の使用例1

 

import os
import sys

def CreateBatfile(wfilename='run.bat',StrData=[]):
    #起動バッチファイルの生成
    s0='"'
    wfile=open(wfilename,'w')
    wfile.write('cd '+s0+str(ScriptPath()[2])+s0+'\n')
    wfile.write('echo off'+'\n')
    wfile.write('python '+s0+str(sys.argv[0])+s0+' '+' '.join(StrData)+'\n')

    writepath=WriteFilePath(Pathname='ReadWRite_Data') #出力場所を指定(パスのみ)
    
    s1="rem EXPLORER "
    s2=writepath
    s=s1+s0+s2+s0
    wfile.write(s+' \n')
    wfile.write('pause \n')
    wfile.flush()
    wfile.close


def WriteFilePath(Pathname='ReadWrite_Data'):     
    writepath=os.path.join(ScriptPath()[2],Pathname)
    if not os.path.isdir(writepath):
        os.mkdir(writepath)
    return writepath

def ScriptPath():
    # ■## ScriptPath():   スクリプトパスを取得
    #ScriptPath()[0]:basename ScriptPath()[1]:pathname ScriptPath()[2]:fulpath  
    basename=os.path.basename((sys.argv[0]))
    pathname=os.path.abspath(os.path.dirname(sys.argv[0]))
    fullpath=os.path.join(pathname,basename)
    return (basename,fullpath,pathname)

#■メインプログラム
if __name__  == '__main__':

    CreateBatfile(wfilename='run.bat',StrData=[])
    print('バッチファイルを生成しました')
    #
    
    a=input('プログラムが終了しました。Enterキーを押してください!')

 

このテストプログラムはD:tmp:python_codeにスクリプトファイルを保存してから、実行したと仮定して、次のようなバッチファイルが出力されます。

 

cd "D:\tmp\python_code"
echo off
python "D:\tmp\python_code\J815_CreateBatfile.py" 
rem EXPLORER "D:\tmp\python_code\ReadWrite_Data" 
pause 

 

CreateBatfile() の使用例2 pythonへの引数追加

上記のメインプログラムの一部を変更して、バッチファイルで、pythonスクリプトに引数を渡せるようにしてみましょう。

#■メインプログラム
if __name__  == '__main__':

    CreateBatfile(wfilename='run.bat',StrData=['123','ABC','日本語の引数'])
    print('バッチファイルを生成しました')
    #
    if len(sys.argv)>1:
        print(sys.argv)
    
    a=input('プログラムが終了しました。Enterキーを押してください!')

関数に渡すStrDataは文字列のリストとなっていますので、下記のように文字列を記述します。

StrData=[‘123′,’ABC’,’日本語の引数’]

このプログラムを実行した場合にバッチファイルの中身は、以下のようになります。

 

cd "D:\tmp\python_code"
echo off
python "D:\tmp\python_code\J815_CreateBatfile.py" 123 ABC 日本語の引数
rem EXPLORER "D:\tmp\python_code\ReadWrite_Data" 
pause 

ちゃんと引数が設定できています。

コードの中で

    if len(sysargv)>1:
        print(sys.argv)

この部分は、もし、pythonプログラムを実行するときに引数が渡されていたらそれを表示するようにしたコードです。少しややこしい話ですが、バッチファイルで実行したときだけ有効なコードであると言えます。つまり、通常のバッチファイルを使わない実行方法のときには、sys.argv[0]にスクリプトファイルのパスが格納されているだけなので、sys.argv変数の個数は1個のため、上記のprint()関数は実行されないということです。(ちょっと複雑な話ですね)

ややこしいですが、pythonにバッチファイルで引数を渡したいときは、このCreateBatfile()関数はとても重宝すると感じるでしょう。

 

超シンプル版 バッチファイル出力コード

いろいろと、応用展開を考えて、CreateBatfile()関数を紹介してきましたが、「わざわざ関数作ってまで、そんなことしたくない! もう~」といわれる方もおられると思います。

そんなときには、次のスニペットをコピー&ペーストで、メインの関数の最初に入れてもらえばいいです。たった三行です。

import sys,os
fname=os.path.splitext(os.path.basename(sys.argv[0]))[0]+'.bat'
with open(fname,'w') as f:f.write(f'python "{sys.argv[0]}" \n pause \n')

すこし解説です。

import sys,os

まずはインポートですね。sysとosをインポートしてください

fname=os.path.splitext(os.path.basename(sys.argv[0]))[0]+'.bat'

つぎは、一行に書いてしまいましたが

  • sys.argv[0]は、スクリプトファイルパスが格納されています
  • os.path.splitext()関数は、ファイル名と拡張子を分離してくれます
  • os.path.splitext(os.path.basename(sys.argv[0]))[0] の[0]がファイル名部分を示します
  • + ”.bat” 
  • 新しくバッチファイルの拡張子をつけて、書き出すファイル名が完成
  • with open(fname,’w’) as f:f.write(f’python “{sys.argv[0]}” \n pause \n’) このように1行に書くべきではありませんが
  • できるだけ行数を減らすためにわざと1行いしました。このコードで、バッチファイルの内容を出力しています。\nは改行で、pauseはバッチファイルを一時停止します。

バッチファイルを書き出すだけが目的なら、これでOKです。とてもシンプルになりました。

コメント