teratail header banner
teratail header banner
質問するログイン新規登録

質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.30%
Windows 10

Windows 10は、マイクロソフト社がリリースしたOSです。Modern UIを標準画面にした8.1から、10では再びデスクトップ主体に戻され、UIも変更されています。PCやスマホ、タブレットなど様々なデバイスに幅広く対応していることが特徴です。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

1994閲覧

Python os.systemだとエラーになるが、subprocess.callだとエラーにならない

kankan0

総合スコア24

Windows 10

Windows 10は、マイクロソフト社がリリースしたOSです。Modern UIを標準画面にした8.1から、10では再びデスクトップ主体に戻され、UIも変更されています。PCやスマホ、タブレットなど様々なデバイスに幅広く対応していることが特徴です。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2023/05/01 13:26

0

0

Windows10において、Python3で以下のようにバッチファイルを起動させたいとします。

python

1os.system(r'"C:\testpy\テスト.bat" "C:\abc\テスト.txt"')

この時に「ファイル名、ディレクトリ名、またはボリューム ラベルの構文が間違っています。」
と表示されエラーになり、実行に失敗します。

そこで、以下のように変更しました。

python

1subprocess.call(r'"C:\testpy\テスト.bat" "C:\abc\テスト.txt"')

ご覧のように、実行文自体は完全に同一ですが、subprocess.callを使用したところエラーなく正常に実行できました。
これは何故でしょうか?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

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.systemshell=Truecmd.exeを呼ぶかどうかは環境変数COMSPECを変更すれば変わりますが、普通の言語のライブラリではcmd.exe以外を想定していないので、もしCOMSPECの値をbash.exeのフルパスに変更してもbash /c 引数文字列を実行するのでbashがエラーメッセージを出します。bashを認識してbash -c 引数文字列に切り替えてくれる言語を知りません(あるかもですが)。

投稿2023/05/01 14:57

otn

総合スコア86328

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kankan0

2023/05/02 11:38

このような変形がされてたとは夢にも思わず、確かによく分からない仕様ですが、大変勉強になりました! ありがとうございました。
otn

2023/05/02 12:48

余談の続きですが、 > bashを認識してbash -c 引数文字列に切り替えてくれる言語を知りません(あるかもですが)。 なんか、思い出しました。 Emacsの今はメンテされてない日本語対応Windows実装で、Meadowというのがあったのですが、このMeadowのEmacs-Lispでは、/c か -c かをオプションで変更できたはずです。cmd.exeかbash.exeかを見て自動切り替えでは無かったと思いますが。 外部プログラム呼び出し時の引用符の扱いも、各種Cコンパイラの仕様に合わせて撰べたはず。日本人らしい細やかさ。本家Emacsが多言語対応したので、終息しましたが。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.30%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問