- 博客/
python脚本中调用shell的几种方法
方法一#
1. os.system(‘cmd’)方法
使用system方法可以返回运行cmd命令的状态返回值,同时会在终端输出运行结果,但无法将执行的结果保存起来
system方法比较适用于执行单个命令、通常没有输出结果的情况
- python交互模式下
>>> os.system('tar cvf /data/1.tar /data/docker')
tar: Removing leading `/' from member names
/data/docker/
/data/docker/auth/
/data/docker/auth/htpasswd
/data/docker/a.sh
0 <-- 命令执行状态返回值
- python脚本中
$ cat sh-system.py
#!/usr/bin/python36
import os,sys
if len(sys.argv) < 3 :
print ('2 arguments is needed')
sys.exit(1)
os.system('./test.sh %s %s'%(sys.argv[1],sys.argv[2])) <--自动打印cmd在linux上执行的信息,无需使用print
#os.system('./test.sh '+sys.argv[1]+' '+sys.argv[2])
$ cat test.sh
#!/usr/bin/bash
echo -e "This is a shell script. \nNAME:$1\tAUTHOR:$2"
运行python脚本,终端结果如下
[root@master python_learning]# ./sh-system.py test tom
This is a shell script.
NAME:test AUTHOR:tom
方法二#
2. commands.getstatusoutput(‘cmd1;cmd2;…’)方法
commands.getstatusoutput方法可以取得命令的输出(包括标准和错误输出)和执行状态位
python3已经使用subprocess取代
- python2交互式模式下
[root@master python_learning]# python
Python 2.7.5 (default, Aug 4 2017, 00:39:18)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os,commands
>>> status,result=commands.getstatusoutput('ls')
>>> status
0
>>> result
'getIP.py\nsh-commands.py\nsh-popen.py\nsh-subprocess.py\nsh-system.py\ntest.sh\nzipdir\nzip.py'
>>> commands.getoutput('ls') <--只返回命令执行结果
'getIP.py\nsh-commands.py\nsh-popen.py\nsh-subprocess.py\nsh-system.py\ntest.sh\nzipdir\nzip.py'
- python脚本中
[root@master python_learning]# cat ./sh-commands.py
#!/usr/bin/python
import os,commands
status,result=commands.getstatusoutput('ls;cat /data/1.txt')
print (status)
print (result)
运行python脚本,终端结果如下
[root@master python_learning]# ./sh-commands.py
256 <--cmd1;cmd2;...中有一个cmd执行失败,状态返回码就不为0
sh-commands.py
sh-system.py
test.sh
cat: /data/1.txt: No such file or directory
方法三#
3.os.popen(‘cmd’)方法
popen(command [, mode=‘r’ [, bufsize]])
-> pipe Open a pipe to/from a command returning a file object. 返回一个类文件对象,调用该对象的read()或readlines()方法可以读取输出内容
command – 使用的命令
mode – 模式权限可以是 ‘r’(默认) 或 ‘w’
bufsize – 指明了文件需要的缓冲大小:0意味着无缓冲;1意味着行缓冲;其它正值表示使用参数大小的缓冲(大概值,以字节为单位)。负的bufsize意味着使用系统的默认值,一般来说,对于tty设备,它是行缓冲;对于其它文件,它是全缓冲。如果没有改参数,使用系统的默认值
- python脚本中
[root@master python_learning]# vim sh-popen.py
#!/usr/bin/env python36
#-*- coding:utf-8 -*-
import os,sys
if len(sys.argv) < 3 :
print ('2 arguments is needed')
sys.exit(1)
cmd = './test.sh %s %s'%(sys.argv[1],sys.argv[2])
print (os.popen(cmd).readlines()) <--返回的是类文件对象,需要调用print打印出来
print (os.popen(cmd).read(),end='')
运行python脚本,终端结果如下
[root@master python_learning]# ./sh-popen.py test tom
['This is a shell script. \n', 'NAME:test\tAUTHOR:tom\n']
This is a shell script.
NAME:test AUTHOR:tom
方法四#
4.subprocess模块
subprocess被用来替换一些老的模块和函数,如:os.system、os.spawn*、os.popen*、popen2.、commands.。所以强烈推荐使用subprocess模块。
subprocess模块中只定义了一个类: Popen。可以使用Popen来创建进程,并与进程进行复杂的交互。它的构造函数如下:
*class subprocess.Popen(args, bufsize=-1, 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,restore_signals=True, start_new_session=False, pass_fds=(), , encoding=None, errors=None)
各参数请参考 Python Documentation中的定义
常用的几个函数subprocess.call()、subprocess.check_call()、subprocess.check_output()、subprocess.Popen()。实际上,上面的几个函数都是基于Popen()的封装(wrapper)。这些封装的目的在于让我们容易使用子进程。
- subprocess.call()
subprocess.call(args,* , stdin=None, stdout=None, stderr=None, shell=False, cwd=None, timeout=None)
Run the command described by args. Wait for command to complete, then return the
returncode
attribute
运行命令,父进程等待子进程完成(阻塞),然后返回returncode(状态码)
[root@master python_learning]# python36
Python 3.6.3 (default, Jan 4 2018, 16:40:53)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import subprocess
>>> subprocess.call(['ls','-l','/data']) <--args序列化的表达方式
total 20
-rw-r--r--. 1 root root 10240 Feb 8 11:06 1.tar
-rw-r--r--. 1 root root 24 Feb 22 14:56 1.txt
drwxr-xr-x. 2 root root 79 Dec 25 09:56 build_images
drwxr-xr-x. 2 root root 6 Jan 15 16:05 container
drwxr-xr-x. 4 root root 30 Jan 10 15:47 dcos
drwxr-xr-x. 3 root root 28 Feb 8 11:06 docker
drwxr-xr-x. 3 root root 4096 Feb 22 15:31 python_learning
drwxr-xr-x. 5 root root 36 Dec 11 11:07 zookeeper
0
>>> subprocess.call('ls -l /data',shell=True) <--args字符串表达,指定shell=True
total 20
-rw-r--r--. 1 root root 10240 Feb 8 11:06 1.tar
-rw-r--r--. 1 root root 24 Feb 22 14:56 1.txt
drwxr-xr-x. 2 root root 79 Dec 25 09:56 build_images
drwxr-xr-x. 2 root root 6 Jan 15 16:05 container
drwxr-xr-x. 4 root root 30 Jan 10 15:47 dcos
drwxr-xr-x. 3 root root 28 Feb 8 11:06 docker
drwxr-xr-x. 3 root root 4096 Feb 22 15:31 python_learning
drwxr-xr-x. 5 root root 36 Dec 11 11:07 zookeeper
0
>>> subprocess.call('ls -l /data/ ; ls notexistfile',shell=True)
total 20
-rw-r--r--. 1 root root 10240 Feb 8 11:06 1.tar
-rw-r--r--. 1 root root 24 Feb 22 14:56 1.txt
drwxr-xr-x. 2 root root 79 Dec 25 09:56 build_images
drwxr-xr-x. 2 root root 6 Jan 15 16:05 container
drwxr-xr-x. 4 root root 30 Jan 10 15:47 dcos
drwxr-xr-x. 3 root root 28 Feb 8 11:06 docker
drwxr-xr-x. 3 root root 4096 Feb 22 15:31 python_learning
drwxr-xr-x. 5 root root 36 Dec 11 11:07 zookeeper
ls: cannot access notexistfile: No such file or directory
2
Tips:
使用shlex.split()方法序列化args
>>> import shlex,subprocess
>>> cmd=input()
/bin/echo -e -n "this a test '$CASE'"
>>> args=shlex.split(cmd)
>>> print (args)
['/bin/echo', '-e', '-n', "this a test '$CASE'"]
>>> p = subprocess.Popen(args)
>>> this a test '$CASE'
- subprocess.check_call()
subprocess.check_call(args, *,stdin=None, stdout=None, stderr=None, shell=False, cwd=None, timeout=None)
Run command with arguments. Wait for command to complete. If the return code was zero then return, otherwise raise
CalledProcessError
. TheCalledProcessError
object will have the return code in thereturncode
attribute.
运行命令,父进程等待子进程完成(阻塞),如果子进程返回的returncode不为0的话,将抛出CalledProcessError异常。在异常对象中,包括进程的returncode信息。
>>> subprocess.check_call('ls -l /data/;ls notexistfile',shell=True)
total 20
-rw-r--r--. 1 root root 10240 Feb 8 11:06 1.tar
-rw-r--r--. 1 root root 24 Feb 22 14:56 1.txt
drwxr-xr-x. 2 root root 79 Dec 25 09:56 build_images
drwxr-xr-x. 2 root root 6 Jan 15 16:05 container
drwxr-xr-x. 4 root root 30 Jan 10 15:47 dcos
drwxr-xr-x. 3 root root 28 Feb 8 11:06 docker
drwxr-xr-x. 3 root root 4096 Feb 22 15:31 python_learning
drwxr-xr-x. 5 root root 36 Dec 11 11:07 zookeeper
ls: cannot access notexistfile: No such file or directory
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib64/python3.6/subprocess.py", line 291, in check_call
raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command 'ls -l /data/;ls notexistfile' returned non-zero exit status 2.
- subprocess.check_output()
subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, cwd=None, encoding=None, errors=None, universal_newlines=False, timeout=None)
Run command with arguments and return its output.
If the return code was non-zero it raises a
CalledProcessError
. TheCalledProcessError
object will have the return code in thereturncode
attribute and any output in theoutput
attribute.
运行命令,父进程等待子进程完成(阻塞),并返回子进程的标准输出,如果子进程返回的returncode不为0的话,将抛出CalledProcessError异常。在异常对象中,包括进程的returncode信息和标准输出的信息
>>> subprocess.check_output('ls -l',shell=True)
b'total 32\n-rw-r--r--. 1 root root 164 Feb 22 19:28 \\\n-rwxr-xr-x. 1 root root 229 Feb 22 10:24 getIP.py\n-rwxr-xr-x. 1 root root 253 Feb 8 13:37 sh-commands.py\n-rwxr-xr-x. 1 root root 369 Feb 22 15:31 sh-popen.py\n-rwxr-xr-x. 1 root root 572 Feb 8 20:44 sh-subprocess.py\n-rwxr-xr-x. 1 root root 407 Feb 22 14:46 sh-system.py\n-rwxr-xr-x. 1 root root 72 Feb 8 13:24 test.sh\ndrwxr-xr-x. 4 root root 42 Feb 8 18:29 zipdir\n-rwxr-xr-x. 1 root root 1046 Feb 8 19:00 zip.py\n'
>>> subprocess.check_output('ls notexistfile',shell=True)
ls: cannot access notexistfile: No such file or directory
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib64/python3.6/subprocess.py", line 336, in check_output
**kwargs).stdout
File "/usr/lib64/python3.6/subprocess.py", line 418, in run
output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command 'ls notexistfile' returned non-zero exit status 2.
使用try和except语句处理subprocess.CalledProcessError异常
#!/usr/bin/env python36
import subprocess
code_flag=0
try:
p2=subprocess.check_output('ls notexistfile',shell=True)
print(p2)
except subprocess.CalledProcessError:
#print ('returncode is not 0')
code_flag=2
if code_flag != 0:
print ('execute cmd failed')
如果要获取标准错误,需定义stderr参数 stderr=subprocess.STDOUT
>>> subprocess.check_output(
... "ls non_existent_file; exit 0",
... stderr=subprocess.STDOUT,
... shell=True)
'ls: non_existent_file: No such file or directory\n'
- subprocess.Popen()
经常的使用方法为subporcess.Popen, 我们可以在Popen()建立子进程的时候改变标准输入、标准输出和标准错误,并可以利用subprocess.PIPE将多个子进程的输入和输出连接在一起,构成管道(pipe):
import subprocess
child1 = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE)
child2 = subprocess.Popen(["wc"], stdin=child1.stdout,stdout=subprocess.PIPE)
out = child2.communicate()
print(out)
subprocess.PIPE
在创建Popen对象时,subprocess.PIPE可以初始化stdin, stdout或stderr参数。表示与子进程通信的标准流,它实际上为文本流提供一个缓存区,缓存区有一定的容量限制,当缓存区满了之后,子进程就会停止写入数据,程序就会卡住
Popen对象创建后,主程序不会自动等待子进程完成。我们必须调用对象的wait()方法,父进程才会等待 (也就是阻塞block)。这里要注意的是如果进程输出文本数据超过buffersize(默认64k),调用wait()方法会使程序锁死,推荐使用communicate()方法(communicate()是Popen对象的一个方法,该方法也会阻塞父进程,直到子进程完成
),这个方法会把输出放在内存,而不是管道里,所以这时候上限就和内存大小有关了,一般不会有问题。而且如果要获得程序返回值,可以在调用 Popen.communicate() 之后取 Popen.returncode 的值。但如果超过内存,那么要考虑比如文件 stdout=open(“process.out”, “w”) 的方式来解决,不能再使用管道了
Popen对象的方法:
Popen.poll() 用于检查子进程是否已经结束。设置并返回returncode属性。
Popen.wait() 等待子进程结束。设置并返回returncode属性。
Popen.communicate(input=None)
与子进程进行交互。向stdin发送数据,或从stdout和stderr中读取数据。可选参数input指定发送到子进程的参数。 Communicate()返回一个元组:(stdoutdata, stderrdata)
。注意:如果希望通过进程的stdin向其发送数据,在创建Popen对象的时候,参数stdin必须被设置为PIPE。同样,如 果希望从stdout和stderr获取数据,必须将stdout和stderr设置为PIPE。
Popen.send_signal(signal) 向子进程发送信号。
Popen.terminate() 停止(stop)子进程。在windows平台下,该方法将调用Windows API TerminateProcess()来结束子进程。
Popen.kill() 杀死子进程。
Popen.stdin 如果在创建Popen对象是,参数stdin被设置为PIPE,Popen.stdin将返回一个文件对象用于策子进程发送指令。否则返回None。
Popen.stdout
如果在创建Popen对象是,参数stdout被设置为PIPE,Popen.stdout将返回一个文件对象用于策子进程发送指令。否则返回 None。(类似于os.popen()方法
)
Popen.stderr 如果在创建Popen对象是,参数stdout被设置为PIPE,Popen.stdout将返回一个文件对象用于策子进程发送指令。否则返回 None。
Popen.pid 获取子进程的进程ID。
Popen.returncode 获取进程的返回值。如果进程还没有结束,返回None。
实例一 :
使用Popen的communicate()方法保存子进程输出信息,避免程序死锁
[root@master python_learning]# vim sh-subprocess.py
#!/usr/bin/env python36
#-*- coding:utf-8 -*-
import os,sys
import subprocess
if len(sys.argv) < 3 :
print ('2 arguments is needed')
sys.exit(1)
cmd = './test.sh %s %s'%(sys.argv[1],sys.argv[2])
p=subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE)
#p.wait() #阻塞父进程,等待cmd执行完成
#print(p.stdout.read()) #从管道读出输出信息
output,errors=p.communicate() #输出信息缓存到内存中;进程结束后,返回元组(stdoutdata, stderrdata)
print(output) #打印stdoutdata
if p.returncode != 0:
print ("execute shell script failed")
实例二 :
env参数默认为None,子进程默认继承父进程的环境变量。但是,一旦你自定义了env的值(类型为字典),则子程序的环境变量全部由env参数决定,与父进程无关
>>> p=subprocess.Popen('echo this is a test "$case"',env={'case':'123456'},shell=True,stdout=subprocess.PIPE)
>>> print(p.stdout.read())
b'this is 123456\n'
方法五#
5.sh模块
参考 官方文档
- 调用常用命令
>>> import sh
>>> sh.ls()
getIP.py sh-popen.py sh-system.py test.sh zip.py
sh-commands.py sh-subprocess.py sub1.py zipdir
>>> sh.ls('-l','./getIP.py') #命令参数以字符串形式传递
-rwxr-xr-x. 1 root root 229 Feb 22 10:24 ./getIP.py
或者
>>> from sh import ls
>>> ls()
getIP.py sh-popen.py sh-system.py test.sh zip.py
sh-commands.py sh-subprocess.py sub1.py zipdir
>>> ls('-l','./getIP.py')
-rwxr-xr-x. 1 root root 229 Feb 22 10:24 ./getIP.py
未完待续。。。