我正在尝试编写一个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
,为什么程序不会运行?
mongod = subprocess.Popen(
["mongod", "--dbpath", path_to_database],
)
但是,如果path_to_database
包含~/
,则仍然无效。换句话说,/Users/myusername/path/to/db
有效,但~/path/to/db
没有。我最初的问题得到了很好的回答,我绝对可以做到这一点,不确定这个新的皱纹是否应该成为一个新的问题...
答案 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
应该是安全的,但一般来说,这是一个好主意。只有在真的需要时才使用它。