在python中打开Mongod,如何避免`shell = True`

时间:2015-04-10 15:30:53

标签: python mongodb shell subprocess

我正在尝试编写一个python脚本,它将启动mongod,创建一个数据库(或打开我已经创建的数据库),添加一些信息,然后关闭mongod。

#!/usr/bin/env python

from pymongo import MongoClient
import subprocess

def create_mongo_database(database_name, path_to_database):
    mongod = subprocess.Popen(
        "mongod --dbpath {0}".format(path_to_database),
        shell=True
    )
    client = MongoClient()
    db = client[database_name]
    collection = db['test_collection']
    collection.insert_one({'something new':'some data'})
    mongod.terminate()

这段代码有效,但是阅读python文档,他们说在子进程中使用shell=True是一个坏主意。我对这些东西很新手,我真的不明白shell=True标志正在做什么,但我知道在输入变量时访问shell是不好的。问题是,当我尝试运行此删除shell=True参数时,我收到以下错误:

Traceback (most recent call last):
  File "/Users/KBLaptop/computation/kvasir/mongo_test2.py", line 23, in <module>
    create_mongo_database('test5_database', '~/computation/db')
  File "/Users/KBLaptop/computation/kvasir/mongo_test2.py", line 12, in create_mongo_database
    "mongod --dbpath {0}".format(path_to_database),
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 709, in __init__
    errread, errwrite)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 1326, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory

不确定是否重要,但在工作案例和失败案例中,我都是在sublime text3脚本末尾用create_mongo_database('test5_database', '~/computation/db')运行它。

所以我的问题是 - 在这种情况下使用shell=True危险吗?如果我不进行shell=True,为什么程序不会运行?

编辑:鉴于Dano和Charles Duffy的解释,我现在将命令改为:

mongod = subprocess.Popen(
    ["mongod", "--dbpath", path_to_database],
)

但是,如果path_to_database包含~/,则仍然无效。换句话说,/Users/myusername/path/to/db有效,但~/path/to/db没有。我最初的问题得到了很好的回答,我绝对可以做到这一点,不确定这个新的皱纹是否应该成为一个新的问题...

2 个答案:

答案 0 :(得分:5)

我实际上对现有答案强烈反对(建议shlex.split())。如果你传入的shell引用的字符串可能包含未知数量的参数,这是有道理的 - 但在这种情况下,你知道完全你想要多少个参数:你想要三个,从不更多或者更少,并且您希望确定path_to_database只成为一个参数。

因此,适当的使用(如果想要波浪扩展行为)是:

mongod = subprocess.Popen(['mongod', '--dbpath', os.path.expanduser(path_to_database)])

否则,包含空格的路径将被拆分为多个参数,并且包含文字引号的路径(它们在UNIX上是合法的)将这些引用视为转义/语法而不是数据。使用shell=True可以完成这两件事 - 使shlex.split()使用默认的shell=False肯定更安全 - 但传递显式数组更好。

答案 1 :(得分:4)

如果您不使用shell=True,则需要将命令拆分为各自的参数。最简单的方法是使用shlex.split

 mongod = subprocess.Popen(
        shlex.split("mongod --dbpath {0}".format(os.path.expanduser(path_to_database)))
    )

编辑:Charles Duffy指出,在这种情况下使用shlex.split对所有可能的路径都不会正常运行。使用shell=False显式传递数组会更好。有关详细信息,请参阅他的回答。

shell=True命令告诉Python使用底层命令提示符(例如bash,sh等)执行命令。 shell=True被认为是危险的原因是因为如果您将用户定义的字符串传递给命令,他们可能会创建一个执行任意代码的命令。因此,在您的示例中,如果用户提供了path_to_database,请想象他们是否通过了这个:"; ls /"。在shell中执行命令时,;字符被视为命令分隔符,除ls /命令外,最终还会执行mongod。显然,这是非常糟糕的。

如果您改为使用shell=False,那么; ls /字符将被视为mongod命令的参数,而不是传递给;的shell有特殊意义。

所有这一切,如果path_to_database不是,而且永远不会是由用户提供的,那么使用shell=True应该是安全的,但一般来说,这是一个好主意。只有在真的需要时才使用它。