python的subprocess:⼦程序调⽤(调⽤执⾏其他命令);获取⼦程序脚本当
前路径问题
python当前进程可以调⽤⼦进程,⼦进程可以执⾏其他命令,如shell,python,java,c...
⽽调⽤⼦进程⽅法有
os模块
参见:blog.csdn/longshenlmj/article/details/8331526
⽽提⾼版是 subprocess模块,类似os的部分功能,可以说是优化的专项功能类.
python subprocess
⽤于程序执⾏时调⽤⼦程序,通过stdout,stdin和stderr进⾏交互。
Stdout⼦程序执⾏结果返回,如⽂件、屏幕等
Stdin ⼦程序执⾏时的输⼊,如⽂件,⽂件对象
Stderr错误输出
常⽤的两种⽅式(以shell程序为例):
1,subprocess.Popen('脚本/shell', shell=True) #⽆阻塞并⾏
2,subprocess.call('脚本/shell', shell=True) #等⼦程序结束再继续
两者的区别是前者⽆阻塞,会和主程序并⾏运⾏,后者必须等待命令执⾏完毕,如果想要前者编程阻塞加wait():
p = subprocess.Popen('脚本/shell', shell=True)
a=p.wait() # 返回⼦进程结果
具体代码事例:
hadoop_cmd = "hadoop fs -ls %s"%(hive_tb_path)
p = subprocess.Popen(hadoop_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
ret = p.wait() #wait()函数是等待模式的执⾏⼦进程,返回执⾏命令状态,成功0,失败1
print ret #执⾏成功返回0,失败返回1。
#⽽命令的结果查看通过
print ad()
#错误查看通过
print ad()
调⽤⼦进程代码实例:
⽅式⼀
import subprocess
p=subprocess.Popen('./test/dirtest.py',stdout=subprocess.PIPE,shell=True)
print adlines()
out,err = pmunicate()
print out
print err
##这是⼀次性交互,读⼊是stdin,直接执⾏完毕后,返回给stdout,communicate通信⼀次之后即关闭了管道。但如果需要多次交互,频繁地和⼦线程通信不能使⽤communicate(),可以分步进⾏通信,如下:
p= subprocess.Popen(["ls","-l"], stdin=subprocess.PIPE,stdout=subprocess.PIPE,shell=False)
//输⼊
p.stdin.write('your command')
p.stdin.flush()
//查看输出
adline()
ad()
⽅式⼆
ret=subprocess.call('ping -c 1 %s' % ip,shell=True,stdout=open('/dev/null','w'),stderr=subprocess.STDOUT)
if ret==0:
print'%s is alive!' %ip
elif ret==1:
print'%s '%ip
参数shell的意义
call()和Popen()都有shell参数,默认为False,可以赋值为True。
参数shell(默认为False)指定是否使⽤shell来执⾏程序。如果shell为True,前⾯会⾃动加上/bin/sh命令,则建议传递⼀个字符串(⽽不是序列)给args,如果为False就必须传列表,分开存储命令内容。⽐如
subprocess.Popen("", shell=True)
相当于
subprocess.Popen(["/bin/sh", "-c", ""])
原因具体是,
在Linux下,shell=False时, Popen调⽤os.execvp()执⾏args指定的程序;
在Windows下,Popen调⽤CreateProcess()执⾏args指定的外部程序,args传⼊字符和序列都⾏,序列会⾃动list2cmdline()转化为字符串,但需要注意的是,并不是MS Windows下所有的程序都可以⽤list2cmdline来转化为命令⾏字符串。
所以,windows下
subprocess.Popen(" " shell=True)
等同于
subprocess.Popen(" /C "+" " shell=True)
shell=True可能引起问题
传递shell=True在与不可信任的输⼊绑定在⼀起时可能出现安全问题
警告执⾏的shell命令如果来⾃不可信任的输⼊源将使得程序容易受到shell注⼊攻击,⼀个严重的安全缺陷可能导致执⾏任意的命令。因为这个原因,在命令字符串是从外部输⼊的情况下使⽤shell=True 是强烈不建议的:
>>> from subprocess import call
>>> filename = input("What file would you like to display?\n")
What file would you like to display?
non_existent; rm -rf / #
>>> call("cat " + filename, shell=True) # Uh-oh. This will
shell=False禁⽤所有基于shell的功能,所以不会受此漏洞影响;参见Popen构造函数⽂档中的注意事项以得到如何使shell=False⼯作的有⽤提⽰。
当使⽤shell=True时,pipes.quote()可以⽤来正确地转义字符串中将⽤来构造shell命令的空⽩和shell元字符。
⼏个介绍subprocess⽐较详细的⽹站:
python.usyiyi/python_278/library/subprocess.html(英⽂/2/library/subprocess.html)
ipseek.blog.51cto/1041109/807513
blog.linuxeye/375.html
blog.csdn/imzoer/article/details/8678029
⼦程序脚本的当前路径问题
不管⽤os还是subprocess调⽤⼦程序,都会遇到获取当前路径的问题。即⼦程序脚本代码中想要获取当前路径,那么获取的路径是主程序还是⼦程序的?Python获取脚本路径的⽅式主要有两种:
1)os.path.dirname(os.path.abspath("__file__"))
2)sys.path[0]
参考blog.csdn/longshenlmj/article/details/25148935,
第⼀种会获取主程序的路径,也就是当前的__file__对象存的是主程序脚本
第⼆种才能获取⼦程序脚本的路径
代码实例:
主程序脚本callpy.py路径为/home/wizad/lmj,
调⽤的⼦程序脚本dirtest.py路径为/home/wizad/lmj/test
[wizad@srv26 lmj]$ cat callpy.py
import subprocess
p = subprocess.Popen('python ./test/dirtest.py',stdout=open('','w'),shell=True)
[wizad@srv26 test]$ cat dirtest.py
import os
import sys
file_path=os.path.dirname(os.path.abspath("__file__"))
调用子程序的例子print file_path+"11111"
cur_path = sys.path[0]
print cur_path+"22222"
执⾏python callpy.py结果输出:
/home/wizad/lmj11111
/home/wizad/lmj/test22222
输出结果是放到⽂件中,可以看出⽅式1是主程序路径,⽽⽅式2是⼦程序路径。 另外,stdout的输出⽅式还可以是PIPE,读取的⽅式可以直接打印,
如,
1)
p = subprocess.Popen('python ./test/dirtest.py',stdout=subprocess.PIPE,shell=True)
out,err = pmunicate()
print out
print err
输出:[wizad@srv26 lmj]$ python callpy.py
/home/wizad/lmj11111
/home/wizad/lmj/test22222
None
2)
p = subprocess.Popen('python ./test/dirtest.py',stdout=subprocess.PIPE,shell=True)
print adlines()
out,err = pmunicate()
print out
print err
输出为
['/home/wizad/lmj11111\n', '/home/wizad/lmj/test22222\n']
None
这两种读取⽅式,是直接通过屏幕输出结果。
有关subprocess模块其他知识,引⽤⼀些资料如下:
subprocess.Popen(
args,
bufsize=0,
executable=None,
stdin=None,
stdout=None,
stderr=None,
preexec_fn=None,
close_fds=False,
shell=False,
cwd=None,
env=None,
universal_newlines=False,
startupinfo=None,
creationflags=0)
1)、args可以是字符串或者序列类型(如:list,元组),⽤于指定进程的可执⾏⽂件及其参数。如果是序列类型,第⼀个元素通常是可执⾏⽂件的路径。我们也可以显式的使⽤executeable参数来指定可执⾏⽂件的路径。
2)、bufsize:指定缓冲。0 ⽆缓冲,1 ⾏缓冲,其他 缓冲区⼤⼩,负值 系统缓冲(全缓冲)
3)、stdin, stdout, stderr分别表⽰程序的标准输⼊、输出、错误句柄。他们可以是PIPE,⽂件描述符或⽂件对象,也可以设置为None,表⽰从⽗进程继承。
4)、preexec_fn只在Unix平台下有效,⽤于指定⼀个可执⾏对象(callable object),它将在⼦进程运⾏之前被调⽤。
5)、Close_sfs:在windows平台下,如果close_fds被设置为True,则新创建的⼦进程将不会继承⽗进程的输⼊、输出、错误管道。我们不能将close_fds设置为True同时重定向⼦进程的标准输⼊、输出与错误(stdin, stdout, stderr)。
6)、shell设为true,程序将通过shell来执⾏。
7)、cwd⽤于设置⼦进程的当前⽬录
8)、env是字典类型,⽤于指定⼦进程的环境变量。如果env = None,⼦进程的环境变量将从⽗进程中继承。Universal_newlines:不同操作系统下,⽂本的换⾏符是不⼀样的。如:windows下⽤’/r/n’表⽰换,⽽Linux下⽤’/n’。如果将此参数设置为True,Python统⼀把这些换⾏符当作’/n’来处理。
9)、startupinfo与createionflags只在windows下有效,它们将被传递给底层的CreateProcess()函数,⽤于设置⼦进程的⼀些属性,如:主窗⼝的外观,进程的优先级等等。
Popen⽅法
1)、Popen.poll():⽤于检查⼦进程是否已经结束。设置并返回returncode属性。
2)、Popen.wait():等待⼦进程结束。设置并返回returncode属性。
3)、Popenmunicate(input=None):与⼦进程进⾏交互。向stdin发送数据,或从stdout和stderr中读取数据。可选参数input指定发送到⼦进程的参数。Communicate()返回⼀个元组:(stdoutdata, stderrdata)。注意:如果希望通过进程的stdin向其发送数据,在创建Popen对象的时候,参数stdin必须被设置为PIPE。同样,如果希望从stdout和stderr获取数据,必须将stdout和stderr设置为PIPE。
4)、Popen.send_signal(signal):向⼦进程发送信号。
5)、inate():停⽌(stop)⼦进程。在windows平台下,该⽅法将调⽤Windows API TerminateProcess()来结束⼦进程。
6)、Popen.kill():杀死⼦进程。
7)、Popen.stdin:如果在创建Popen对象是,参数stdin被设置为PIPE,Popen.stdin将返回⼀个⽂件对象⽤于策⼦进程发送指令。否则返回None。
8)、Popen.stdout:如果在创建Popen对象是,参数stdout被设置为PIPE,Popen.stdout将返回⼀个⽂件对象⽤于策⼦进程发送指令。否则返回None。
9)、Popen.stderr:如果在创建Popen对象是,参数stdout被设置为PIPE,Popen.stdout将返回⼀个⽂件对象⽤于策⼦进程发送指令。否则返回None。
10)、Popen.pid:获取⼦进程的进程ID。
11)、urncode:获取进程的返回值。如果进程还没有结束,返回None。
12)、subprocess.call(*popenargs, **kwargs):运⾏命令。该函数将⼀直等待到⼦进程运⾏结束,并返回进程的returncode。⽂章⼀开始的例⼦就演⽰了call函数。如果⼦进程不需要进⾏交互,就可以使⽤该函数来创建。
13)、subprocess.check_call(*popenargs, **kwargs):与subprocess.call(*popenargs, **kwargs)功能⼀样,只是如果⼦进程返回的returncode不为0的话,将触发CalledProcessError异常。在异常对象中,包括进程的returncode信息。
死锁
使⽤管道时,不去处理管道的输出,当⼦进程输出了⼤量数据到stdout或者stderr的管道,并达到了系统pipe的缓存⼤⼩的话(操作系统缓存⽆法获取更多信息),⼦进程会等待⽗进程读取管道,⽽⽗进程此时正wait着的话,将会产⽣传说中的死锁。
可能引起死锁的调⽤:
subprocess.call()
subprocess.check_call()
subprocess.check_output()
Popen.wait()
可以看出,⼦进程使⽤管道交互,如果需要等待⼦进程完毕,就可能引起死锁。⽐如下⾯的⽤法:
p=subprocess.Popen("longprint", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
p.wait()
longprint是⼀个假想的有⼤量输出的进程,那么在我的xp, Python2.5的环境下,当输出达到4096时,死锁就发⽣了。
避免subprocess的管道引起死锁
1)使⽤Popen()和communicate()⽅法,可以避免死锁。没有等待,会⾃动清理缓存。
2)如果⽤adline(或者pmunicate)去清理输出,那么⽆论输出多少,死锁都是不会发⽣的。
3)或者不⽤管道,⽐如不做重定向,或者重定向到⽂件,也可以避免死锁。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论