Python执行外部程序

《Python核心编程》总结了几种在Python中调用外部程序的方法。xmuruijie使用的是另外一种方法,它用的是commands模块的getoutput方法。总结下在Python中调用外部程序的几种方法吧。

1、os模块的exec方法族。Python的exec系统方法同Unix的exec系统调用是一致的。这些方法适用于在子进程中调用外部程序的情况,因为外部程序会替换当前进程的代码,不会返回。

2、使用os模块的system方法。system方法会创建子进程运行外部程序,方法只返回外部程序的运行结果。这个方法比较适用于外部程序没有输出结果的情况。比如在Ubuntu下,使用下面命令在桌面上显示一条提示信息。

?
1
os.system( "notify-send 'hello world'" )

3、使用os模块的popen方法。当需要得到外部程序的输出结果时,本方法非常有用。比如使用urllib调用Web API时,需要对得到的数据进行处理。一个使用例子如下:

?
01
02
03
04
05
06
07
08
09
10
11
12
cmd = "ssh search47c.cm2 \"" + query + "\"" ;
  
#print cmd + "<br>"
  
output = os.popen(cmd);
  
#对特殊字符进行转义
temp1 = output.read().replace( '<' , '&lt;' )
temp2 = temp1.replace( '>' , "&gt;" );
temp3 = temp2.replace( '\n' , "<br>" );
  
print temp3.replace( '/' , "&#47" );

4、使用commands模块的getoutput方法,这种方法同popend的区别在于popen返回的是一个文件句柄,而本方法将外部程序的输出结果当作字符串返回,很多情况下用起来要更方便些。在xmuruijie中,ruijie类有两个方法用到了这个方法,分别用于获得网关地址和DNS服务器地址,代码如下所示:

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
def gateway( self ):
'''
return the Default Gateway of given interface in binary
'''
gw = getoutput( "netstat -rn | grep  '%s' | grep 'UG' | awk '{print $2}'" % self .iface )
if gw:
     return socket.inet_aton( gw )
else :
     return ''
  
def dns( self ):
'''
return the First DNS in binary
'''
dns_d = getoutput( "cat /etc/resolv.conf | sed -n '/^nameserver/{p;q}' | awk '{print $2}'" )
if dns_d:
     return socket.inet_aton( dns_d )
else :
     return ''

5、使用subprocess模块,这个模块比较复杂,可以对子进程做更多控制。根据Python官方文档说明,subprocess模块用于取代上面这些模块。有一个用Python实现的并行ssh工具—mssh,代码很简短,不过很有意思,它在线程中调用subprocess启动子进程来干活。

?
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#!/usr/bin/python
#LICENSE GPLv3
#VERSION 1.0
#VERSION 2.0, June 18, 2009
#VERSION 2.1, June 24, 2009, '--thread' and 'mssh.config'
#Author Manhong Dai, University of Michigan
import sys, optparse, subprocess, threading, os.path
__version__ = "mssh 2.1"
class NodeCmd(threading.Thread):
     def __init__( self , node, cmd, dtOut):
         threading.Thread.__init__( self )
         self .node = node
         self .cmd = cmd
         self .dtOut = dtOut
     def run( self ):
         p = subprocess.Popen([ 'ssh' , self .node, self .cmd], stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
         stdout = p.communicate()[ 0 ]
         self .dtOut[ self .node] = (p.returncode, stdout)
              
def main():
     parser = optparse.OptionParser()
     parser.add_option( "-s" , "--setname" ,
         help = "Name of a set of nodes" )
     parser.add_option( "-e" , "--exclude" ,
         help = "A list of nodes to be exluded" )
     parser.add_option( "-t" , "--thread" ,
         help = "run N threads, <=0 means one thread for each node" , type = 'int' )
     parser.add_option( "-l" , "--list" ,
         action = "store_true" ,
         help = "list all set name" )
     parser.add_option( "-v" , "--version" ,
         action = "store_true" ,
         help = "print version information" )
     (options, args) = parser.parse_args()
     dtSet = {}
     for ln in file ( '%s/mssh.config' % os.path.abspath(os.path.dirname(sys.argv[ 0 ]))):
         fs = ln.split( ':' )
         if len (fs) ! = 2 :
             print >> sys.stderr, 'line mssh.config is wrong\n' , ln,
         dtSet[fs[ 0 ]] = [n.strip() for n in fs[ 1 ].split( ',' )]
     ALL = []
     for v in dtSet.values():
         ALL = ALL + v
     ALL = list ( set ( ALL ))
     ALL .sort()
     dtSet[ 'ALL' ] = ALL
     if options.version = = True :
         print __version__
     elif options. list = = True :
         sk = dtSet.keys()
         sk.sort()
         for k in sk:
             sv = dtSet[k]
             sys.stdout.write( '%s:\t%s' % (k, sv[ 0 ]))
             for v in sv[ 1 :]:
                 sys.stdout.write( ',%s' % v)
             sys.stdout.write( '\n' )
     elif options.setname = = None :
         parser.error( 'setname is mandatory' )
     elif len (args) = = 0 :
         parser.error( 'there is no parameters' )
     else :
         if options.exclude ! = None :
             ex =  options.exclude.split( ',' )
         else :
             ex = []
         cmd = args[ 0 ]
         for a in args[ 1 :]: cmd = '%s %s' % (cmd, a)
         nodes = []
         for st in options.setname.split( ',' ):
             st = st.strip()
             if st not in dtSet: parser.error( 'st is not a valid set name, use --list to find out' )
             nodes + = dtSet[st]
         nodes = list ( set (nodes))
         nodes.sort()
         thrds, dtOut = [], {}
         for nd in nodes:
             if nd not in ex: thrds.append(NodeCmd(nd, cmd, dtOut))
         print '#####################################################################'
         print '##############################Waiting for############################'
         if options.thread < = 0 : options.thread = len (thrds)
         i = 0
         while i < len (thrds):
             for t in thrds[i:i + options.thread]:
                 t.start()
             for t in thrds[i:i + options.thread]:
                 print t.node
                 t.join()
             i + = options.thread
         nonzero = []
         print '#####################################################################'
         print '##############################Normal output##########################'
         for t in thrds:
             node = t.node
             (rtn, stdout) = dtOut[node]
             if rtn = = 0 :
                 if stdout ! = None :
                     for ln in stdout.split( '\n' ): print '%s:\t%s' % (node, ln)
             else :
                 nonzero.append(node)
         if len (nonzero) > 0 :
             print '#####################################################################'
             print '##############################Nonzero output#########################'
             for t in thrds:
                 node = t.node
                 (rtn, stdout) = dtOut[node]
                 if rtn ! = 0 :
                     if stdout ! = None :
                         for ln in stdout.split( '\n' ): print '%s:\t%s' % (node, ln)
             print '#####################################################################'
             print '##############################nonzero list###########################'
             for n in nonzero: print n,
if __name__ = = '__main__' :
     main()

看到一段代码非常有意思,让人看不出来数据从哪里传递给进程的。主程序如下:

?
1
2
3
4
5
#!/bin/bash
  
read line
  
printf "is $line\n"

主程序代码如下:

?
1
2
3
4
5
6
7
8
9
#!/usr/bin/env python
  
#import subprocess
  
def nohup(cmd, log = None ):
     """ Execute command like nohup but return the instance of Popen. """
     return subprocess.Popen(cmd, shell = True , bufsize = 8192 , close_fds = True , stdout = log, stderr = log)
  
nohup( "./test_read.sh" )

将一个字符串通过管道送给subproc.py,发现test_read.sh会将这个字符串打印出来,因为子进程继承了父进程的标准输入输出,所以test_read.sh能从标准输入中获得字符串。

?
1
echo "nihaoya" | . /subproc .py

除了上述五种方法之外,还可以使用dl和ctypes两个模块直接调用动态库中的函数,其中dl只能用于Unix-like平台,而ctypes则是通用的。ctypes比较适合写胶水代码,C++做个split都不容易。在霜天同学的帮助下,把代码改成了python,用起来非常方便。

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
extern "C" {
  
char buf[128]={0};
  
DLLEXPORT int init(){
         if ( pw.Init()==0){
                 return 0;
         }
         else
                 return -1;
}
  
DLLEXPORT const char * getPids( const char *t, const char * c, int cat_id){
         std::vector<pstruct> pids;
         std::string title=t;
         std::string content=c;
  
         if (pw.getPids(title, content, "" , pids, cat_id) == 0)
         {
                 if (pids.capacity()>0 && pids.begin()!=pids.end())
                 {
                         sprintf (buf, "%d,%d" ,  pids.begin()->pid, pids.begin()->crm_pid);
  
                         return buf;
                 }
         }
  
         return 0;
}
}

调用方法如下:

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
from ctypes import *
  
pidmatch = CDLL( "match.so" )
init = pidmatch.init
init.restype = c_int
  
#指明函数参数及返回值类型
getpids = pidmatch.getPids
getpids.argtype = [c_char_p, c_int]
getpids.restype = c_char_p
  
ids = getPids(title, spu_prop, int (cat_id));
  
if ids:
     epid, spuid = ids.split( "," )
else :
     epid, spuid = '0' , '0'
转自 http://www.fuzhijie.me/?p=207
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值