在我的本地机器上,我运行一个包含这一行的python脚本
bashCommand = "cwm --rdf test.rdf --ntriples > test.nt"
os.system(bashCommand)
Run Code Online (Sandbox Code Playgroud)
这很好用.
然后我在服务器上运行相同的代码,我收到以下错误消息
'import site' failed; use -v for traceback
Traceback (most recent call last):
File "/usr/bin/cwm", line 48, in <module>
from swap import diag
ImportError: No module named swap
Run Code Online (Sandbox Code Playgroud)
所以我所做的就是插入一个"print bashCommand",它在使用os.system()运行它之前打印我,而不是终端中的命令.
当然,我再次得到错误(由os.system(bashCommand)引起)但在该错误之前它在终端中打印命令.然后我只是复制了那个输出并在终端上做了一个复制粘贴,然后点击回车就可以了......
有没有人知道发生了什么?
use*_*312 285
不要用os.system
.它已被弃用,有利于子进程.从文档:"这个模块打算替换几个较旧的模块和功能:os.system
,os.spawn
".
就像你的情况一样:
bashCommand = "cwm --rdf test.rdf --ntriples > test.nt"
import subprocess
process = subprocess.Popen(bashCommand.split(), stdout=subprocess.PIPE)
output, error = process.communicate()
Run Code Online (Sandbox Code Playgroud)
tri*_*eee 112
为了稍微扩展这里的早期答案,有许多细节常被忽视.
subprocess.run()
在subprocess.check_call()
和朋友过subprocess.call()
过subprocess.Popen()
过os.system()
过os.popen()
text=True
,又名universal_newlines=True
.shell=True
或者shell=False
它如何改变引用和shell便利性的可用性.sh
和Bash 之间的差异下面将详细介绍这些主题.
subprocess.run()
或subprocess.check_call()
该subprocess.Popen()
函数是一个低级别的主力,但是使用正确并且你最终复制/粘贴多行代码......这很方便地已经存在于标准库中,作为一组用于各种目的的高级包装函数,以下更详细地介绍.
这是文档中的一段:
调用子进程的推荐方法是将
run()
函数用于它可以处理的所有用例.对于更高级的用例,Popen
可以直接使用底层接口.
不幸的是,这些包装函数的可用性在Python版本之间有所不同.
subprocess.run()
在Python 3.5中正式引入.它旨在取代以下所有内容.subprocess.check_output()
在Python 2.7/3.1中引入.它基本上相当于subprocess.run(..., check=True, stdout=subprocess.PIPE).stdout
subprocess.check_call()
是在Python 2.5中引入的.它基本上相当于subprocess.run(..., check=True)
subprocess.call()
在原始subprocess
模块(PEP-324)中引入了Python 2.4 .它基本上相当于subprocess.run(...).returncode
subprocess.Popen()
重构和扩展subprocess.run()
比它取代的旧的遗留功能更具逻辑性和更多功能.它返回一个CompletedProcess
对象,该对象具有各种方法,允许您从完成的子流程中检索退出状态,标准输出以及一些其他结果和状态指示器.
subprocess.run()
如果你只是需要一个程序来运行并将控制权交还给Python,那就是你要走的路.对于更复杂的场景(后台进程,可能还有使用Python父程序的交互式I/O),您仍然需要自己使用subprocess.Popen()
并处理所有管道.这需要对所有运动部件进行相当复杂的理解,不应轻易进行.更简单的Popen
对象表示(可能仍在运行)进程,需要在代码中管理子进程的剩余生命周期.
或许应该强调的是,subprocess.Popen()
仅仅创造一个过程.如果你把它留在那,你有一个与Python同时运行的子进程,所以一个"后台"进程.如果它不需要输入或输出或以其他方式与您协调,它可以与您的Python程序并行执行有用的工作.
os.system()
和os.popen()
由于时间永恒的(当然,因为Python 2.5)的os
模块文件包含了更倾向于推荐subprocess
过os.system()
:
该
subprocess
模块提供了更强大的工具来产生新流程并检索其结果; 使用该模块比使用此功能更可取.
问题system()
在于它显然依赖于系统,并且不提供与子进程交互的方法.它只是运行,标准输出和标准错误超出了Python的范围.Python收到的唯一信息是命令的退出状态(零表示成功,尽管非零值的含义也在某种程度上取决于系统).
PEP-324(上面已经提到过)包含了更详细的理由,说明为什么os.system
有问题以及如何subprocess
解决这些问题.
os.popen()
曾经更加强烈劝阻:
从版本2.6开始不推荐使用:此功能已过时.使用该
subprocess
模块.
但是,从Python 3的某个时候开始,它已重新实现,只需使用subprocess
,并重定向到subprocess.Popen()
文档以获取详细信息.
check=True
你还会注意到它subprocess.call()
有许多相同的限制os.system()
.在常规使用中,通常应该检查进程是否成功完成,哪个subprocess.check_call()
和哪个subprocess.check_output()
(后者也返回完成的子进程的标准输出).同样,除非特别需要允许子进程返回错误状态check=True
,subprocess.run()
否则通常应该使用.
实际上,使用check=True
或者subprocess.check_*
,如果子进程返回非零退出状态,Python将抛出CalledProcessError
异常.
如果子进程失败,下游代码失败时,常见的错误subprocess.run()
是省略check=True
并感到惊讶.
在另一方面,有一个共同的问题check_call()
和check_output()
是谁盲目使用这些功能的用户是当异常发生,例如当惊讶grep
没有找到匹配.(grep
无论如何,您应该用原生Python代码替换,如下所述.)
所有事情都计算在内,您需要了解shell命令如何返回退出代码,以及在什么条件下它们将返回非零(错误)退出代码,并有意识地决定应该如何处理它.
text=True
akauniversal_newlines=True
从Python 3开始,Python内部的字符串是Unicode字符串.但是不能保证子进程会生成Unicode输出或字符串.
(如果差异不是很明显,建议使用Ned Batchelder的实用Unicode,如果不是直接强制性的话,请阅读.如果您愿意,可以在链接后面进行36分钟的视频演示,尽管自己阅读页面可能会花费更少的时间. )
在内心深处,Python必须获取bytes
缓冲区并以某种方式解释它.如果它包含一大堆二进制数据,则不应将其解码为Unicode字符串,因为这是容易出错和导致错误的行为 - 恰恰是那种在许多Python 2脚本中出现问题的麻烦行为.正确区分编码文本和二进制数据.
有了text=True
,你告诉Python你实际上期望在系统的默认编码中支持文本数据,并且它应该被解码成Python(Unicode)字符串,以达到Python的最佳能力(通常是UTF-8,适用于任何适度的日期系统,除了Windows?)
如果这是不是你要求什么时候回来,Python将只是给你bytes
的字符串stdout
和stderr
字符串.也许稍后您会知道它们毕竟是文本字符串,并且您知道它们的编码.然后,您可以解码它们.
normal = subprocess.run([external, arg],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
check=True,
text=True)
print(normal.stdout)
convoluted = subprocess.run([external, arg],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
check=True)
# You have to know (or guess) the encoding
print(convoluted.stdout.decode('utf-8'))
Run Code Online (Sandbox Code Playgroud)
Python 3.7 text
为关键字参数引入了更短,更具描述性和可理解性的别名,这在以前有些误导性地被称为universal_newlines
.
shell=True
vsshell=False
有了shell=True
你一个字符串传递给你的壳,壳从那里拿过来.
随着shell=False
你传递的参数列表的OS,绕过外壳.
当你没有shell时,你可以保存一个进程并消除相当多的隐藏复杂性,这可能会或可能不会存在错误甚至是安全问题.
另一方面,当您没有shell时,您没有重定向,通配符扩展,作业控制和大量其他shell功能.
一个常见的错误是使用shell=True
然后仍然向Python传递令牌列表,反之亦然.这种情况在某些情况下会起作用,但实际上定义不明确,可能会以有趣的方式破解.
# XXX AVOID THIS BUG
buggy = subprocess.run('dig +short stackoverflow.com')
# XXX AVOID THIS BUG TOO
broken = subprocess.run(['dig', '+short', 'stackoverflow.com'],
shell=True)
# XXX DEFINITELY AVOID THIS
pathological = subprocess.run(['dig +short stackoverflow.com'],
shell=True)
correct = subprocess.run(['dig', '+short', 'stackoverflow.com'],
# Probably don't forget these, too
check=True, text=True)
# XXX Probably better avoid shell=True
# but this is nominally correct
fixed_but_fugly = subprocess.run('dig +short stackoverflow.com',
shell=True,
# Probably don't forget these, too
check=True, text=True)
Run Code Online (Sandbox Code Playgroud)
常见的反驳"但它对我有用"并不是一个有用的反驳,除非你完全明白在什么情况下它可能会停止工作.
通常,shell的功能可以用本机Python代码替换.简单的Awk或sed
脚本应该简单地转换为Python.
为了部分地说明这一点,这里是一个典型但略显愚蠢的例子,涉及许多shell特征.
cmd = '''while read -r x;
do ping -c 3 "$x" | grep 'round-trip min/avg/max'
done <hosts.txt'''
# Trivial but horrible
results = subprocess.run(
cmd, shell=True, universal_newlines=True, check=True)
print(results.stdout)
# Reimplement with shell=False
with open('hosts.txt') as hosts:
for host in hosts:
host = host.rstrip('\n') # drop newline
ping = subprocess.run(
['ping', '-c', '3', host],
text=True,
stdout=subprocess.PIPE,
check=True)
for line in ping.stdout.split('\n'):
if 'round-trip min/avg/max' in line:
print('{}: {}'.format(host, line))
Run Code Online (Sandbox Code Playgroud)
有些事情需要注意:
shell=False
你不需要引用的外壳需要大约字符串.无论如何放置引号可能是一个错误.重构的代码还用非常简洁的语法说明了shell为你做了多少 - 无论好坏.蟒蛇说明确优于隐式,但Python代码是相当冗长,可以说是看起来复杂得多,这确实是.另一方面,它提供了许多要点,您可以在其他内容中获取控制权,正如我们可以轻松地包含主机名和shell命令输出的增强功能所示.(这绝不是在shell中做的挑战,而是以另一个转移和另一个过程为代价.)
为了完整起见,这里有一些shell功能的简要说明,以及有关如何用原生Python工具替换它们的一些注释.
glob.glob()
或通常使用简单的Python字符串比较for file in os.listdir('.'): if not file.endswith('.png'): continue
.Bash有各种其他扩展工具,如.{png,jpg}
大括号扩展和{1..100}
波浪扩展(~
扩展到您的主目录,更一般~account
地扩展到另一个用户的主目录)$SHELL
打开$my_exported_var
用于写入和os.environ['SHELL']
读取,并将其内容作为标准输入传递给export
,其标准输出随后进入env=
.这通常很难用原生Python代码替换.subprocess
运行两个子进程,其中标准输出shell=False
是标准输入cd "$HOME"
(在OS级别,在类Unix系统中,这是单个文件句柄).如果你不能用本机Python代码替换管道的一端或两端,或许考虑使用shell,特别是如果管道有两个或三个以上的进程(虽然查看os.chdir(os.environ['HOME'])
Python标准库中的模块或数字更现代和多才多艺的第三方竞争对手).cd
和Bash 之间的差异grep 'foo' <inputfile >outputfile
outputfile
除非你特别要求,否则运行你的shell命令(除了当然在Windows上,它使用inputfile
变量的值).这意味着不能使用各种类似Bash的功能,如数组grep
等.
如果您需要使用仅Bash语法,则可以将路径传递给shell outputfile
(当然,如果您的Bash安装在其他地方,则需要调整路径).
subprocess.run('''
# This for loop syntax is Bash only
for((i=1;i<=$#;i++)); do
# Arrays are Bash-only
array[i]+=123
done''',
shell=True, check=True,
executable='/bin/bash')
Run Code Online (Sandbox Code Playgroud)
echo foo | nl
与其父项分开,无法更改一个常见的错误是做类似的事情
subprocess.run('foo=bar', shell=True)
subprocess.run('echo "$foo"', shell=True) # Doesn't work
Run Code Online (Sandbox Code Playgroud)
除了缺乏优雅之外,还缺乏对"子过程"这个名称的"子"部分的基本缺乏理解.
子进程完全独立于Python运行,当它完成时,Python不知道它做了什么(除了它可以从退出状态和子进程的输出推断出的模糊指示).孩子一般不能改变父母的环境; 它不能设置变量,更改工作目录,或者用很多词来与父节点进行通信,而无需与父节点协作.
在这种特殊情况下,立即修复是在单个子进程中运行这两个命令;
subprocess.run('foo=bar; echo "$foo"', shell=True)
Run Code Online (Sandbox Code Playgroud)
虽然这个特殊用例显然根本不需要shell.请记住,您可以通过操纵当前进程(以及它的子进程)的环境
os.environ['foo'] = 'bar'
Run Code Online (Sandbox Code Playgroud)
或者将环境设置传递给子进程
subprocess.run('echo "$foo"', shell=True, env={'foo': 'bar'})
Run Code Online (Sandbox Code Playgroud)
(更不用说显而易见的重构echo
;但nl
当然首先是在子进程中运行的一个糟糕的例子).
这是一个有点可疑的建议; 在某些情况下,它确实有意义,或者甚至是将Python解释器作为Python脚本的子进程运行的绝对要求.但很多时候,正确的方法是pipes
将其他Python模块简单地放入调用脚本并直接调用其函数.
如果其他Python脚本在您的控制之下,并且它不是模块,请考虑将其转换为一个模块.(这个答案已经太久了,所以我不会在这里深入研究细节.)
如果需要并行性,可以使用ls -l /
模块在子进程中运行Python函数. 还有'ls' '-l' '/'
一个在单个进程中运行多个任务(它更轻量级并且为您提供更多控制,但在进程中的线程紧密耦合并绑定到单个GIL时也更受限制.)
Jak*_*yer 38
用子进程调用它
import subprocess
subprocess.Popen("cwm --rdf test.rdf --ntriples > test.nt")
Run Code Online (Sandbox Code Playgroud)
你得到的错误似乎是因为服务器上没有交换模块,你应该在服务器上安装swap然后再次运行脚本
Raz*_*zor 17
您可以使用bash程序,使用参数-c执行命令:
bashCommand = "cwm --rdf test.rdf --ntriples > test.nt"
output = subprocess.check_output(['bash','-c', bashCommand])
Run Code Online (Sandbox Code Playgroud)
小智 9
你可以使用'subprocess',但我一直觉得这不是'Pythonic'的做法.所以我创建了Sultan(无耻插件),可以轻松运行命令行功能.
https://github.com/aeroxis/sultan
小智 6
你也可以使用'os.popen'。例子:
import os
command = os.popen('ls -al')
print(command.read())
print(command.close())
Run Code Online (Sandbox Code Playgroud)
输出:
total 16
drwxr-xr-x 2 root root 4096 ago 13 21:53 .
drwxr-xr-x 4 root root 4096 ago 13 01:50 ..
-rw-r--r-- 1 root root 1278 ago 13 21:12 bot.py
-rw-r--r-- 1 root root 77 ago 13 21:53 test.py
None
Run Code Online (Sandbox Code Playgroud)
要在没有 shell 的情况下运行命令,请将命令作为列表传递并使用 Python 实现重定向[subprocess]
:
#!/usr/bin/env python
import subprocess
with open('test.nt', 'wb', 0) as file:
subprocess.check_call("cwm --rdf test.rdf --ntriples".split(),
stdout=file)
Run Code Online (Sandbox Code Playgroud)
注意:最后没有> test.nt
。stdout=file
实现重定向。
要在 Python 中使用 shell 运行命令,请将命令作为字符串传递并启用shell=True
:
#!/usr/bin/env python
import subprocess
subprocess.check_call("cwm --rdf test.rdf --ntriples > test.nt",
shell=True)
Run Code Online (Sandbox Code Playgroud)
这里的shell负责输出重定向(> test.nt
在命令中)。
要运行使用 bashisms 的 bash 命令,请明确指定 bash 可执行文件,例如,模拟 bash 进程替换:
#!/usr/bin/env python
import subprocess
subprocess.check_call('program <(command) <(another-command)',
shell=True, executable='/bin/bash')
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
471055 次 |
最近记录: |