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

2024-04-29 04:27:24 发布

您现在位置:Python中文网/ 问答频道 /正文

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

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

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

我只是在说执行简单的单行shell命令。当我们需要对任务的执行进行更多的控制时,我理解使用subprocess模块更有意义,例如。


Tags: 文件方法命令功能目录osshellcall
3条回答

这样比较安全。下面是一个脚本示例

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

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

这就是为什么使用内置函数更安全的原因。

因此,为什么您也应该使用子流程而不是系统。

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

  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. 因为Python的标准库应该为您处理这些问题,所以跨平台和跨shell的关注点更少。您的系统是否有chmod命令?是否已安装?它是否支持您希望它支持的参数?如果不可能的话,os模块将尝试尽可能跨平台和文档。

  6. 如果您运行的命令有您关心的输出,您需要解析它,这比听起来更难,因为您可能会忘记角大小写(文件名中有空格、制表符和换行符),即使您不关心可移植性。

在执行命令时,在^{}模块中优先使用Python更具体的方法的情况有四种:

  • 冗余-生成另一个进程是冗余的,浪费时间和资源。
  • 可移植性-模块中的许多方法在多个平台上可用,而许多shell命令是操作系统特有的。
  • 了解结果-生成一个进程来执行任意命令会迫使您分析输出结果,并了解如果和为什么某个命令做错了什么。
  • 安全性-进程可以潜在地执行其给定的任何命令。这是一个弱设计,可以通过在os模块中使用特定的方法来避免。

冗余(见redundant code):

实际上,您正在执行一个多余的“中间人”以到达最终的系统调用(在您的示例中是chmod)。这个中间人是一个新的过程或子壳。

来自^{}

Execute the command (a string) in a subshell ...

^{}只是一个生成新进程的模块。

你可以做你需要的,而不产生这些过程。

可移植性(见source code portability):

^{}模块的目标是提供通用操作系统服务,它的描述以以下开头:

This module provides a portable way of using operating system dependent functionality.

在windows和unix上都可以使用^{}。尝试为此功能使用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")

有许多这样的命令会使您的系统处于危险之中。

相关问题 更多 >