LoginSignup
11
7

More than 5 years have passed since last update.

僕とcpとSubprocessと

Last updated at Posted at 2014-01-30

subprocess回りで少しハマったのでメモ。

デプロイ用のヘルパースクリプトの作成中に、cp -pr相当の処理が必要になった。Pythonで書く(不完全な)cp実装は以下の通り。

python_cp
def _copy(src, dst):
    if os.path.isfile(src):
        shutil.copy2(src, dst)
        return
    if os.path.isdir(src) and os.path.isdir(dst):
        target = os.path.join(dst, os.path.basename(src))
        # TODO Fix behavior like cp -pr (targetディレクトリが存在していた場合、その下にあるファイル/ディレクトリは保持されるべき)
        if os.path.exists(target):
            shutil.rmtree(target)
        shutil.copytree(src, target)
        return
    if os.path.isdir(src) and os.path.isfile(dst):
        raise IOError(
            "(Src:directory/Dest:file) This combination isn't allowed")

制約(コメント参照)はあるものの、今回の用途ではそのケースは発生しえなかったため、まあOK。

しかし「うわっ、Subprocessでcp動かせばいいのに、それしないエンジニア(笑)の人って…」という幻聴が、いつもの如く横から聞こえてきたため、subprocessを使う実装も書いてみることとなった。

subprocess_cp_error
def _copy(src, dst):
    subprocess.Popen(['cp', '-pr', src, dst]).communicate()

ただ、この実装だとsrcにワイルドカードが使えない。ワイルドカードを解釈するためには、Popenにshell=Trueを渡し、コマンドをシェルを介して実行する必要がある。
http://docs.python.jp/2.7/library/subprocess.html#subprocess.Popen

subprocess_cp_error2
def _copy(src, dst):
    subprocess.Popen(['cp', '-pr', src, dst], shell=True).communicate()

で、「これでFinish?」と思いきや、そんなことはなく。上の実装だとcpコマンドが全く動かなくなる。
shell=Trueとした場合、前述の通りシェルを介してコマンドが実行されるわけだが、それは以下のコマンドと等価になる。

via_shell_error
/bin/sh -c 'cp' '-pr' (src) (dst)

一見すると良さそうだが、これだと-prはcpのオプションとしてみなされず、src, dstもcpの引数と見なされない。つまり、cpを何の引数もなく実行したことになり、エラーになる。
正しくは、以下のように一つの文字列で指定してやらなくてはならない

via_shell
/bin/sh -c 'cp -pr (src) (dst)'

で、これに対応するsubprocessを実行するには、Popenに与えるコマンドを、配列ではなく文字列で指定すればよい。

subprocess_cp
def _copy(src, dst):
    subprocess.Popen('cp -pr {0} {1}'.format(src, dst), shell=True).communicate()

結論

必要なければshell=Trueにするべきでなしw
まあ実際、シェルインジェクションの原因にもなるので、「外部入力を使うときには絶対Trueにすんなよ!」とライブラリリファレンスにも書いてあるし…

11
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
7