天天看點

Python調用Shell的常見操作和實時輸出

# 第一種,使用os.system方法,這種方法無法擷取輸出結果

import os
os.system('ls')
           

# 第二種,使用os.popen方法,這種方法可以擷取輸出

import os
stream = os.popen('echo 12345') #popen也有不同版本
output = stream.read()
print(output) #輸出12345           

# 第三種,使用subprocess子產品

class subprocess.Popen(args,bufsize=-1,shell=False,stdin=None,stdout=None,stderr=None)
'''
args:對應的shell指令
bufsize:緩沖區大小,0表示不使用緩沖區
shell:若該參數為True,将使用作業系統的shell執行指令
stdin,stdout,stderr分别表示程式的标準輸入,輸出,錯誤句柄
-poll():檢查程序是否終止,終止則傳回returncode,否則None
-wait(timeout):等待子程序終止
-communicate(timeout):和子程序互動
-terminate():停止子程序==send_signal(SIGTERM)
-kill():殺死子程序
'''

# 下面是範例
# 擷取實時輸出

import subprocess
import shlex
def real_run_command(command):
process =subprocess.Popen(shlex.split(command),stdout = subprocess.PIPE) #同時使用了subprocess.PIPE 作為參數,這個是一個特殊值,用于表明這些通道要開放
while True:
output = process.stdout.readline()
if output == b"" and process.poll() is not None: # 因為輸出的位元組流,是以要用b''或者bytes.decode(str,errors='ignore')
break
if output:
print(output.strip())
rc = process.poll()
return rc

#調用
rc = real_run_command(sh test.sh)
print("return code=%s"%rc)           

第四種:ssh實時輸出

#SSH

Python 執行遠端主機可以使用 paramiko 架構,但 paramiko 架構的 exec_command 方法, 預設是沒有開啟 bufsize 的, 也就是說必須等到一個指令執行完, 我們才可以列印到指令的輸出資訊, 但為了體驗更接近在終端執行的感覺, 實時輸出就很有必要了。我這裡的需求是 websockets 實時輸出遠端指令的日志資訊,是以我隻需要定義 command 和下面的 callback 函數就可以了。

Paramiko 的 exec_command 方法提供了 bufsize 參數, 我們可以調小緩沖區, 然後使程式更快的打滿緩沖區生成緩沖塊的方式, 來實作實時輸出。我們對SSHClient 簡單封裝一下, 增加一個 run 的方法。

Python

from itertools import izip_longest
from paramiko import SSHClient
from paramiko import AutoAddPolicy


class MySSHClient(SSHClient):

    def run(self, command, callback):
        stdin, stdout, stderr = self.exec_command(
            command, bufsize=1
        )

        stdout_iter = iter(stdout.readline, '')
        stderr_iter = iter(stderr.readline, '')

        for out, err in izip_longest(stdout_iter, stderr_iter):
            if out: callback(out.strip())
            if err: callback(err.strip())

        return stdin, stdout, stderr           

使用方式和原生的 SSHClient 一樣, 隻不過不去調用 exec_command 方法了, 改為調用 run 方法.

Python

def console(text):
    print(text)

ssh = MySSHClient()
ssh.set_missing_host_key_policy(AutoAddPolicy())
ssh.connect("IPADDRESS", 22, "USER", "PASSWORD")
stdin, stdout, stderr = ssh.run("python -u test.py", console)

print stderr.channel.recv_exit_status()