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

Kod*_*rok 156 python linux operating-system

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

例如,为什么我要使用os.chmod而不是做os.system("chmod...")

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

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

Fli*_*imm 325

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

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

  3. 您不必担心shell 解释的特殊字符.os.chmod(path, mode)无论文件名是什么都可以工作,但os.spawn("chmod 777 " + path)如果文件名是这样的话会失败; rm -rf ~.(请注意,您可以解决这个,如果你使用subprocess.call不带shell参数).

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

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

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

  • 要添加到"跨平台"点,列出目录是Linux上的"ls",Windows上的"dir".获取目录的内容是一项非常常见的低级任务. (38认同)
  • @CortAmmon:对,并且`ls`比这更高级,因为它不是直接调用你的操作系统的内核API.这是一个(小)应用程序. (5认同)

iPr*_*ram 133

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

import os
file = raw_input("Please enter a file: ")
os.system("chmod 777 " + file)
Run Code Online (Sandbox Code Playgroud)

如果来自用户的输入是test; rm -rf ~,那么将删除主目录.

这就是使用内置函数更安全的原因.

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

  • 或者另一种看待它的方法,更容易做到正确,编写Python程序或编写编写shell脚本的Python程序?:-) (26认同)
  • @SteveJessop,我的一位同事惊讶于我帮他写的一个小Python脚本的工作速度提高了20(!)倍.我解释说输出重定向可能看起来很性感 - 但它需要在每次迭代时打开和关闭文件.但有些人喜欢用艰难的方式做事 - :) (3认同)

Reu*_*ani 60

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

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

冗余(参见冗余代码):

您实际上是在执行最终系统调用的过程chmod中执行冗余的"中间人"(在您的示例中).这个中间人是一个新的过程或子壳.

来自os.system:

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

并且subprocess只是一个产生新流程的模块.

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

可移植性(参见源代码可移植性):

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

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

您可以os.listdir在Windows和unix上使用.尝试使用os.system/ subprocess用于此功能将强制您维护两个调用(对于ls/ dir)并检查您正在使用的操作系统.这不是那么便携,以后引起更多的挫折(参见处理输出).

理解命令的结果:

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

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

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

如果您没有列出文件的权限怎么办?

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

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

这也是继的例子不要重复自己原则通过(通常下文称"干")重复已经存在,并且是免费提供给你一个实现.

安全:

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

注射安全性(见壳注射实例):

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

一个例子是表单的命令:

# ... read some user input
os.system(user_input + " some continutation")
Run Code Online (Sandbox Code Playgroud)

这可以很容易利用来运行任何使用输入任意代码:NASTY COMMAND;#创建最终的:

os.system("NASTY COMMAND; # some continuation")
Run Code Online (Sandbox Code Playgroud)

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

  • 我会说2.是主要原因. (3认同)

vol*_*ano 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
Run Code Online (Sandbox Code Playgroud)

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

EDIT2

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


小智 16

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

  • @Koderok废话,模块函数在进程中调用 (7认同)
  • @Koderok:os模块使用shell命令使用的底层系统调用,它不使用shell命令.这意味着os系统调用通常比shell命令更安全,更快(没有字符串解析,boo fork,没有exec,而只是内核调用).请注意,在大多数情况下,shell调用和系统调用通常具有相似或相同的名称,但是单独记录; shell调用在man部分1(默认man部分)中,而等效命名的系统调用在man部分2中(例如man 2 chmod). (3认同)

MSa*_*ers 11

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

当你使用os.system不是内置shell的东西时情况会更糟.你启动一个shell进程,然后启动一个可执行文件,然后(两个进程)进行系统调用.至少subprocess可以消除对shell中间过程的需求.

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