为什么要使用Python的os模块方法而不是直接执行shell命令?

时间:2015-02-17 23:00:29

标签: python linux operating-system

我试图了解使用Python的库函数执行特定于操作系统的任务(如创建文件/目录,更改文件属性等)的动机是什么,而不是仅通过os.system()或{执行这些命令{1}}?

例如,为什么我要使用subprocess.call()而不是os.chmod

我知道尽可能多地使用Python的可用库方法而不是直接执行shell命令更“pythonic”。但是,从功能的角度来看,还有其他动机吗?

我只是在谈论在这里执行简单的单行shell命令。当我们需要更多地控制任务的执行时,我理解使用os.system("chmod...")模块更有意义,例如。

6 个答案:

答案 0 :(得分:325)

  1. 更快os.systemsubprocess.call创建新流程,这对于这么简单的事情是不必要的。实际上,带有os.system参数的subprocess.callshell通常会创建至少两个新进程:第一个是shell,第二个是您正在运行的命令(如果它不是像[{1}})那样的内置shell。

  2. 某些命令在单独的进程中无用。例如,如果运行test,它将更改子进程的当前工作目录,但不会更改Python进程的当前工作目录。您需要使用os.spawn("cd dir/")

  3. 您不必担心shell解释的特殊字符。无论文件名是什么,os.chdir都会有效,如果文件名类似于os.chmod(path, mode)os.spawn("chmod 777 " + path)将会失败。 (请注意,如果使用; rm -rf ~而不使用subprocess.call参数,则可以解决此问题。)

  4. 您不必担心以破折号开头的文件名。 shell将更改名为os.chmod("--quiet", mode)的文件的权限,但--quiet将失败,因为os.spawn("chmod 777 --quiet")被解释为参数。即使--quiet也是如此。

  5. 您的跨平台和跨shell问题较少,因为Python的标准库应该为您处理。您的系统是否有subprocess.call(["chmod", "777", "--quiet"])命令?它安装了吗?它是否支持您希望它支持的参数? chmod模块将尝试尽可能跨平台,并在不可能时提供文档。

  6. 如果你正在运行的命令有输出,你需要解析它,这比它听起来更棘手,因为你可能会忘记角落的情况(文件名)即使您不关心可移植性,也可以使用空格,制表符和换行符。

答案 1 :(得分:133)

更安全。这里给出一个想法是一个示例脚本

import os
file = raw_input("Please enter a file: ")
os.system("chmod 777 " + file)

如果用户输入为test; rm -rf ~,则会删除主目录。

这就是使用内置功能更安全的原因。

因此,为什么你应该使用subprocess而不是system。

答案 2 :(得分:60)

在执行命令时,os模块中使用os.systemsubprocess模块优先选择Python更具体的方法有四种情况:

  • 冗余 - 产生另一个流程是多余的,浪费时间和资源。
  • 可移植性 - os模块中的许多方法都可以在多个平台上使用,而许多shell命令都是特定于操作系统的。
  • 了解结果 - 生成执行任意命令的流程会强制您解析输出结果,并了解 if why a命令做错了。
  • 安全性 - 进程可以执行它给出的任何命令。这是一种弱设计,可以通过使用os模块中的特定方法来避免。

冗余(参见redundant code):

您实际上在前往最终系统调用的路上执行了一个冗余的“中间人”(在您的示例中为chmod)。这个中间人是一个新的过程或子壳。

来自os.system

  

在子shell中执行命令(字符串)...

subprocess只是一个产生新流程的模块。

您可以在不产生这些过程的情况下完成所需的工作。

便携性(见source code portability):

os模块的目标是提供通用的操作系统服务,其描述始于:

  

此模块提供了一种使用操作系统相关功能的便携方式。

您可以在Windows和unix上使用os.listdir。尝试将os.system / subprocess用于此功能会强制您维持两次调用(对于ls / dir)并检查您所使用的操作系统。这不是那么便携,会导致以后更加沮丧(参见处理输出)。

了解命令的结果:

假设您要列出目录中的文件。

如果您使用os.system("ls") / subprocess.call(['ls']),则只能返回进程的输出,这基本上是一个包含文件名的大字符串。

如何从两个文件中告诉文件中带有空格的文件?

如果您无权列出文件,该怎么办?

如何将数据映射到python对象?

这些只是我的头脑,虽然有这些问题的解决方案 - 为什么再次解决一个为你解决的问题?

这是一个遵循Don't Repeat Yourself原则(经常被称为“DRY”)的示例,重复已经存在且可以免费使用的实现。

安全性:

os.systemsubprocess功能强大。当你需要这种力量时它很好,但是当你不需要它时它很危险。当您使用os.listdir时,知道除了列出文件或引发错误之外,它无法执行任何其他操作。当您使用os.systemsubprocess来实现相同的行为时,您最终可能会做出您不想做的事情。

注射安全性(见shell injection examples

如果你使用来自用户的输入作为新命令,你基本上给了他一个shell。这很像SQL注入,在DB中为用户提供shell。

一个例子是形式的命令:

# ... read some user input
os.system(user_input + " some continutation")

使用输入:NASTY COMMAND;#可以轻松利用任意任意代码来创建最终代码:

os.system("NASTY COMMAND; # some continuation")

有许多此类命令可能会使您的系统面临风险。

答案 3 :(得分:23)

出于一个简单的原因 - 当你调用一个shell函数时,它会创建一个在你的命令存在后被销毁的子shell,所以如果你在shell中更改目录 - 它不会影响你在Python中的环境。

此外,创建子shell非常耗时,因此直接使用OS命令会影响性能

修改

我正在进行一些计时测试:

In [379]: %timeit os.chmod('Documents/recipes.txt', 0755)
10000 loops, best of 3: 215 us per loop

In [380]: %timeit os.system('chmod 0755 Documents/recipes.txt')
100 loops, best of 3: 2.47 ms per loop

In [382]: %timeit call(['chmod', '0755', 'Documents/recipes.txt'])
100 loops, best of 3: 2.93 ms per loop

内部功能运行速度提高10倍以上

<强> EDIT2

在某些情况下,调用外部可执行文件可能会产生比Python软件包更好的结果 - 我只记得我的一位同事发来的邮件,通过子进程调用的 gzip 的性能远远高于性能他使用过的Python包。但当我们谈论模拟标准OS命令的标准OS包时,当然不会这样做

答案 4 :(得分:16)

Shell调用是特定于操作系统的,而在大多数情况下,Python os模块函数不是。它避免产生子进程。

答案 5 :(得分:11)

效率更高。 &#34; shell&#34;只是另一个包含大量系统调用的OS二进制文件。为什么只为单个系统调用产生创建整个shell进程的开销?

当您使用os.system用于不是内置shell的内容时,情况会更糟。你启动一个shell进程,然后启动一个可执行文件,然后(两个进程)进行系统调用。至少subprocess将不再需要shell中介进程。

这不是Python特有的。 systemd对Linux启动时间的改进是出于同样的原因:它使得必要的系统调用本身而不是产生一千个shell。