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

在python中打开Mongod,如何避免`shell=True`,python,mongodb,shell,subprocess,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( "mongo

我正在尝试编写一个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

但是,如果数据库的路径包含~/,这仍然不起作用。换句话说,
/Users/myusername/path/to/db
起作用,但
~/path/to/db
不起作用。我最初的问题得到了很好的回答,我完全可以做到这一点,不确定这个新问题是否应该成为新问题……

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

Edit:Charles Duffy指出,在这种情况下,使用
shlex.split
将无法正确处理所有可能的路径。最好只显式地传递带有
shell=False
的数组。更多信息请参见他的答案

shell=True
命令告诉Python使用底层命令提示符(例如bash、sh等)执行命令。之所以认为
shell=True
是危险的,是因为如果将用户定义的字符串传递到命令中,它们可能会生成一个执行任意代码的命令。因此,在您的示例中,如果用户提供了数据库的路径,想象一下他们是否传递了这个:
“ls/”
。在shell中执行命令时,
字符被视为命令分隔符,除执行
mongod
命令外,您还将执行
ls/
。显然,这是非常糟糕的

如果改用
shell=False
,则
;ls/
字符将被视为
mongod
命令的参数,而不是传递给shell,其中
具有特殊的含义


综上所述,如果
path\u to_database
不是也永远不会由用户提供,那么使用
shell=True
应该是安全的,但一般来说,只有在您确实需要的时候才使用它是一个好主意。

我实际上非常不同意现有的答案(建议
shlex.split()
)。如果传入了一个带shell引号的字符串,该字符串可能包含未知数量的参数,那么这是有意义的——但在本例中,您确切地知道需要多少个参数:您需要三个,而不是更多或更少,并且您要确保
path\u to\u database
仅成为一个参数

因此,适当的使用方法(如果需要瓷砖扩展行为)是:


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

@CharlesDuffy是的,你是对的。关于
shell=True
shell=False
之间的区别,我会留下我的答案,但是在这种情况下,使用显式数组肯定比使用shlex.split
要好。我同意——这个解释很有用;编辑后,这是我的投票。@dano这非常清楚,谢谢你的解释!但有一个新的皱纹-请看我的编辑。不确定我是否应该提出一个新问题…@kevbonham用于处理
“~”
。思考实验:如果您的数据库位于名为
“/computation/$(rm-rf.)/db”
的目录中,会发生什么情况?我想我的硬盘上可能什么都没有了?那会很有趣…是的(最坏的情况,取决于细节)!但实际上,正是由
shell=True
调用的shell实现了这种扩展;如果没有shell,那么名称将被视为一个文本文件名;我也适当地扩展了我的答案。糟了,我不能把两种解释都标记为回答。。。我想读这两本书(以及你的假设书)有助于我理解。有一个新的皱纹虽然-请看看我的编辑。
mongod = subprocess.Popen(
    ["mongod", "--dbpath", path_to_database],
)
 mongod = subprocess.Popen(
        shlex.split("mongod --dbpath {0}".format(os.path.expanduser(path_to_database)))
    )
mongod = subprocess.Popen(['mongod', '--dbpath', os.path.expanduser(path_to_database)])