os.system
は、C:\Windows\System32\cmd.exe /c
を呼び出すのですが、cmd.exeの引用符の使い方は特殊です。コマンドプロンプトで、cmd /?
を実行して表示されるヘルプには、
text
1/C または /K が指定されている場合、スイッチの後の残りのコマンド ラインが
2コマンド ラインとして処理されます。次のルールが引用符 (") の処理に使われます:
3
4 1. 次のすべての条件に一致する場合、コマンド ラインの引用符が有効になり
5 ます:
6
7 - /S スイッチがない
8 - 引用符が 1 組ある
9 - 引用符の中に特殊文字がない
10 (特殊文字は &<>()@^| です)
11 - 引用符の中に 1 つ以上のスペースがある
12 - 引用符の中の文字列が、実行可能ファイルの名前である
13
14 2. 最初の文字が引用符であるにも関わらず上の条件に一致しない場合は、最初
15 の引用符とコマンド ラインの最後の引用符が削除され、最後の引用符の後
16 のテキストが有効になります。
とあります。引用符が3つ以上ある場合は、項番2に該当して、訳のわからない変形をされてしまいます。
"C:\testpy\テスト.bat" "C:\abc\テスト.txt"
⇒ C:\testpy\テスト.bat" "C:\abc\テスト.txt
(この仕様の意図がわかる方がいたら説明して欲しい)
コマンドプロンプト画面で、
CMD
1cmd /c "C:\testpy\テスト.bat" "C:\abc\テスト.txt"
と打ち込んでも同じエラーメッセージが出るかと思います。
回避策としては、
ファイル名を引用符で囲む必要があるのは、ディレクトリ名やファイル名に空白等を含んでいる場合なので、含んでいなければ引用符を外して引用符無しか1組だけにすれば良いし、空白等を含んでいて囲むことが必須なら、全体をさらに囲みます。
Python
1os.system(r'"C:\testpy\テスト.bat" C:\abc\テスト.txt')
2os.system(r'""C:\testpy\テスト.bat" "C:\abc\テスト.txt""')
subprocess.call
は、shell=True
を指定しない限りcmd.exeを経由しません。
shell=True
を指定した場合は、C:\Windows\System32\cmd.exe /c "~~~"
と自動的に引数のコマンドライン全体を引用符で囲んでくれるようです。
質問のコードのsubprocess.call
の使い方は、shell=True
を指定していないのに第一引数がリストでなく文字列ですが、これはsubprocess.call
の使い方的に間違っていて、文字列はコマンドラインじゃなくて、実行ファイル名として扱われるというのが本来の仕様です。
実際、Linuxだと、subprocess.call("echo 123")
は、FileNotFoundError: [Errno 2] No such file or directory: 'echo 123'
というエラーになります。
Windowsの場合、Pythonが呼び出すOSのAPIが文字列をコマンドラインと解釈してくれるので、通りますが。
Pythonのリファレンスを読む限り、os.system
ではなくsubprocess
ライブラリを使用することが推奨されており、さらにsubprocess.call
は古いとされ、新しいのはsubprocess.run
です。
なお、os.system
やshell=True
でcmd.exe
を呼ぶかどうかは環境変数COMSPEC
を変更すれば変わりますが、普通の言語のライブラリではcmd.exe
以外を想定していないので、もしCOMSPEC
の値をbash.exeのフルパス
に変更してもbash /c 引数文字列
を実行するのでbashがエラーメッセージを出します。bashを認識してbash -c 引数文字列
に切り替えてくれる言語を知りません(あるかもですが)。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2023/05/02 11:38
2023/05/02 12:48