我试图了解使用Python的库函数执行特定于操作系统的任务(如创建文件/目录,更改文件属性等)的动机是什么,而不是仅通过os.system()
或{执行这些命令{1}}?
例如,为什么我要使用subprocess.call()
而不是os.chmod
?
我知道尽可能多地使用Python的可用库方法而不是直接执行shell命令更“pythonic”。但是,从功能的角度来看,还有其他动机吗?
我只是在谈论在这里执行简单的单行shell命令。当我们需要更多地控制任务的执行时,我理解使用os.system("chmod...")
模块更有意义,例如。
答案 0 :(得分:325)
更快,os.system
和subprocess.call
创建新流程,这对于这么简单的事情是不必要的。实际上,带有os.system
参数的subprocess.call
和shell
通常会创建至少两个新进程:第一个是shell,第二个是您正在运行的命令(如果它不是像[{1}})那样的内置shell。
某些命令在单独的进程中无用。例如,如果运行test
,它将更改子进程的当前工作目录,但不会更改Python进程的当前工作目录。您需要使用os.spawn("cd dir/")
。
您不必担心shell解释的特殊字符。无论文件名是什么,os.chdir
都会有效,如果文件名类似于os.chmod(path, mode)
,os.spawn("chmod 777 " + path)
将会失败。 (请注意,如果使用; rm -rf ~
而不使用subprocess.call
参数,则可以解决此问题。)
您不必担心以破折号开头的文件名。 shell
将更改名为os.chmod("--quiet", mode)
的文件的权限,但--quiet
将失败,因为os.spawn("chmod 777 --quiet")
被解释为参数。即使--quiet
也是如此。
您的跨平台和跨shell问题较少,因为Python的标准库应该为您处理。您的系统是否有subprocess.call(["chmod", "777", "--quiet"])
命令?它安装了吗?它是否支持您希望它支持的参数? chmod
模块将尝试尽可能跨平台,并在不可能时提供文档。
如果你正在运行的命令有输出,你需要解析它,这比它听起来更棘手,因为你可能会忘记角落的情况(文件名)即使您不关心可移植性,也可以使用空格,制表符和换行符。
答案 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.system
或subprocess
模块优先选择Python更具体的方法有四种情况:
os
模块中的许多方法都可以在多个平台上使用,而许多shell命令都是特定于操作系统的。os
模块中的特定方法来避免。您实际上在前往最终系统调用的路上执行了一个冗余的“中间人”(在您的示例中为chmod
)。这个中间人是一个新的过程或子壳。
来自os.system
:
在子shell中执行命令(字符串)...
而subprocess
只是一个产生新流程的模块。
您可以在不产生这些过程的情况下完成所需的工作。
os
模块的目标是提供通用的操作系统服务,其描述始于:
此模块提供了一种使用操作系统相关功能的便携方式。
您可以在Windows和unix上使用os.listdir
。尝试将os.system
/ subprocess
用于此功能会强制您维持两次调用(对于ls
/ dir
)并检查您所使用的操作系统。这不是那么便携,会会导致以后更加沮丧(参见处理输出)。
假设您要列出目录中的文件。
如果您使用os.system("ls")
/ subprocess.call(['ls'])
,则只能返回进程的输出,这基本上是一个包含文件名的大字符串。
如何从两个文件中告诉文件中带有空格的文件?
如果您无权列出文件,该怎么办?
如何将数据映射到python对象?
这些只是我的头脑,虽然有这些问题的解决方案 - 为什么再次解决一个为你解决的问题?
这是一个遵循Don't Repeat Yourself原则(经常被称为“DRY”)的示例,不重复已经存在且可以免费使用的实现。
os.system
和subprocess
功能强大。当你需要这种力量时它很好,但是当你不需要它时它很危险。当您使用os.listdir
时,知道除了列出文件或引发错误之外,它无法执行任何其他操作。当您使用os.system
或subprocess
来实现相同的行为时,您最终可能会做出您不想做的事情。
注射安全性(见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。