86587
Pythonのsubprocessライブラリを使ってファイルをパスワード付きで圧縮したいです。

コマンドでは問題なく実行できますが、subprocessを利用すると下記のエラーが表示されます。

コマンドで実行した場合:

```

    "C:\Program Files\7-Zip\7z.exe" -pPassword data.zip "C:\Users\user1\testfolder"
    
```

**Python プログラムを実行した場合のエラーメッセージ:**

```

    'C:\Program' は、内部コマンドまたは外部コマンド、
    操作可能なプログラムまたはバッチ ファイルとして認識されていません。
    
```

Pythonのプログラム

```

    from distutils import command
    import subprocess
     
    #ZIPファイルプログラム
    fileprogram=r"C:\Program Files\7-Zip\7z.exe" 
    
    #ZIPファイル保存先
    file=r"C:\Users\user1\testfolder"
    
    #全体のコマンド
    allcommand=fileprogram+'a -pPassword data.zip'+file
    #print(exp_message)
    
    #コマンド実行
    result = subprocess.run(allcommand,encoding='shift jis',shell=True,stdout=subprocess.PIPE)
    
    #コマンド実行結果
    command_output = result.stdout
    print(command_output)
    
```

代わりにsubprocess.runから下記のように変更しましたが、  
同じエラー内容が表示されます。

```

    subprocess.call(allcommand,encoding='shift jis',shell=True,stdout=subprocess.PIPE)
    
```

もし分かる方がいましたら、教えていただけると幸いです。

お手数ですが、よろしくお願い致します。
CC BY-SA 4.0
2022-02-26T03:25:24.967
86582
2022-02-27T08:00:32.477
2022-02-27T08:00:32.477
3060
18859
question
python subprocess
Python から外部コマンドを呼び出した際に認識されない
コマンドや引数の区切り文字は空白です。  
コマンドや引数に空白が含まれている場合は区切り文字と解釈されないように、二重引用符で囲む必要があります。  
Pythonは引用符として`'`も使えます。  
以下のようにすればエラーは解消すると思います。

```

    # ZIPファイルプログラム
    fileprogram = r'"C:\Program Files\7-Zip\7z.exe"'
    
```

* * *

質問の「Pythonのプログラム」には他にも問題があります。  
7zipのスイッチ`a`の前に空白が必要です。  
`data.zip`の後にも空白が必要です。

```

    #全体のコマンド
    allcommand = fileprogram + ' a -pPassword data.zip ' + file
    
```
CC BY-SA 4.0
2022-02-26T07:09:30.033
86587
2022-02-26T07:09:30.033
35558
86582
answer
`subprocess.run`に`shell=True`を指定して文字列形式でコマンドを与えるのは避けましょう。

[リファレンス](https://docs.python.org/ja/3/library/subprocess.html#security-
considerations)にも以下のように記載されています。

> args
> はすべての呼び出しに必要で、文字列あるいはプログラム引数のシーケンスでなければなりません。一般に、引数のシーケンスを渡す方が望ましいです。なぜなら、モジュールが必要な引数のエスケープやクオート
> (例えばファイル名中のスペースを許すこと) の面倒を見ることができるためです。単一の文字列を渡す場合、shell は True でなければなりません
> (以下を参照)。

文字列形式でやると、質問のようにスペースにまつわる問題が起きます。さらに、文字列形式でコマンドを渡すには`shell=True`を指定する必要がありますが、これもワイルドカードなどにまつわる問題やセキュリティホールにつながります。そもそも、文字列組み立てはミスをしやすいという一般的な問題もあります。

なので、「subprocess.runに文字列でコマンドを渡さない」というのは基本中の基本として覚えておいてください。外部プログラムを呼び出すモジュールには大抵同じ問題を引き起こす使い方があるので、subprocess.run（やpython)以外を使うときにも気をつけてください。

リファレンスに記載の通り`subprocess.run`はシーケンスを受け取ることができます。

```

    allcommand=[fileprogram, 'a', '-pPassword', 'data.zip', file]
    result = subprocess.run(allcommand,encoding='shiftjis',stdout=subprocess.PIPE)
    
```

コマンドやオプションにスペースが含まれていても意識する必要はありません。
CC BY-SA 4.0
2022-02-27T07:40:32.383
86597
2022-02-27T07:40:32.383
5793
86582
answer