在 python 内打开 Mongod,如何避免 `shell=True`
Opening Mongod within python, how to avoid `shell=True`
我正在尝试编写一个 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
无效。我最初的问题得到了很好的回答,我绝对可以完成这项工作,不确定这个新问题是否应该成为一个新问题...
如果您不使用 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 中的命令时,;
字符被视为命令分隔符,除了 mongod
命令之外,您最终会执行 ls /
。显然,这很糟糕。
如果您改用 shell=False
,那么 ; ls /
字符将仅被视为 mongod
命令的参数,而不是传递给 shell,其中 ;
具有特殊含义。
综上所述,如果 path_to_database
不是并且永远不会由用户提供,那么使用 shell=True
应该是安全的,但一般来说,最好只如果确实需要,请使用它。
我实际上非常不同意现有的答案(建议 shlex.split()
)。如果您传入的 shell 引号字符串可能包含未知数量的参数,那么这是有道理的——但在这种情况下,您 确切地 知道您想要多少个参数: 你想要三个,不多也不少,你想确定 path_to_database
只是一个参数。
因此,适当的使用(如果想要波浪线扩展行为)是:
mongod = subprocess.Popen(['mongod', '--dbpath', os.path.expanduser(path_to_database)])
否则,包含空格的路径将被拆分为多个参数,包含文字引号(它们在 UNIX 上是合法的)的路径会将这些引号视为 escaping/syntax 而不是数据。使用 shell=True
可以做这两个事情,甚至更多——使 shlex.split()
和默认的 shell=False
肯定更安全——但传递一个显式数组更好。
我正在尝试编写一个 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
无效。我最初的问题得到了很好的回答,我绝对可以完成这项工作,不确定这个新问题是否应该成为一个新问题...
如果您不使用 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 中的命令时,;
字符被视为命令分隔符,除了 mongod
命令之外,您最终会执行 ls /
。显然,这很糟糕。
如果您改用 shell=False
,那么 ; ls /
字符将仅被视为 mongod
命令的参数,而不是传递给 shell,其中 ;
具有特殊含义。
综上所述,如果 path_to_database
不是并且永远不会由用户提供,那么使用 shell=True
应该是安全的,但一般来说,最好只如果确实需要,请使用它。
我实际上非常不同意现有的答案(建议 shlex.split()
)。如果您传入的 shell 引号字符串可能包含未知数量的参数,那么这是有道理的——但在这种情况下,您 确切地 知道您想要多少个参数: 你想要三个,不多也不少,你想确定 path_to_database
只是一个参数。
因此,适当的使用(如果想要波浪线扩展行为)是:
mongod = subprocess.Popen(['mongod', '--dbpath', os.path.expanduser(path_to_database)])
否则,包含空格的路径将被拆分为多个参数,包含文字引号(它们在 UNIX 上是合法的)的路径会将这些引号视为 escaping/syntax 而不是数据。使用 shell=True
可以做这两个事情,甚至更多——使 shlex.split()
和默认的 shell=False
肯定更安全——但传递一个显式数组更好。