【问题标题】:Opening Mongod within python, how to avoid `shell=True`在python中打开Mongod,如何避免`shell=True`
【发布时间】:2015-04-10 15:30:52
【问题描述】:

我正在尝试编写一个 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 无效。我最初的问题得到了很好的回答,我绝对可以做到这一点,不确定这个新的皱纹是否应该成为一个新的问题......

【问题讨论】:

  • 思想实验:如果您的数据库位于名为 "/computation/$(rm -rf .)/db" 的目录中会发生什么?
  • 我想我的硬盘上什么都没有了?那会很有趣...
  • 是的(最坏的情况,取决于细节)!但实际上是由shell=True 调用的shell 支持该扩展;如果没有外壳,则名称仅被视为文字文件名。
  • 回复:您对问题的扩展,请参阅docs.python.org/2/library/os.path.html#os.path.expanduser;我也适当地扩展了我的答案。

标签: python mongodb shell subprocess


【解决方案1】:

我实际上非常不同意现有的答案(建议shlex.split())。如果您传入了一个可能包含未知数量参数的 shell 引用字符串,这是有道理的 - 但在这种情况下,您确切地知道您想要多少个参数:您想要三个,永远不会更多或更少,并且您想确定path_to_database 仅成为一个参数。

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

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

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

【讨论】:

  • 糟透了,我无法将两种解释都标记为回答...我认为阅读两者(以及您的假设)有助于我理解。不过有一个新的问题 - 请看看我的编辑。
【解决方案2】:

如果您不使用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 应该是安全的,但一般来说,最好只在真正需要时使用它到。

【讨论】:

  • @CharlesDuffy 是的,你是对的。对于shell=Trueshell=False 之间差异的解释,我将留下答案,但在这种情况下,使用显式数组肯定比shlex.split 更好。
  • 我同意——这个解释很有用;经编辑,这是我的赞成票。
  • @dano 这个超级清楚,谢谢解释!但是有一个新的皱纹 - 请参阅我的编辑。不知道我是否应该提出一个新问题......
  • @kevbonham 使用os.path.expanduser(path_to_database) 处理"~"
猜你喜欢
  • 2015-10-25
  • 2023-03-30
  • 2018-06-14
  • 2019-11-26
  • 1970-01-01
  • 2022-09-26
  • 2012-12-06
  • 2017-04-27
  • 1970-01-01
相关资源
最近更新 更多