pyhton的winrm庫提供了命令行遠程連接的功能,可以實現(xiàn)遠程登錄并進行執(zhí)行指令的功能:
1.遠端登錄
import winrm
auth = (username, password)
shell = winrm.Session(host_ip, auth=auth, transport='ntlm')
當然,如果想實現(xiàn)遠端連接,客戶端和服務端都要進行winrm服務的開啟和設置,詳細請參考:
2.執(zhí)行命令
winrm作為面向window遠程連接的庫,提供了兩種執(zhí)行命令的執(zhí)行方式,cmd命令和ps命令,兩種命令還是有區(qū)別的,例如ps的命令直接用cmd跑就會報錯,二者的區(qū)別筆者借鑒了網(wǎng)上的說法:
powershell 可以看作 cmd 的超集,所有的常用命令諸如dir, cd, ipconfig等在 powershell 中都能直接使用。但背后的實現(xiàn)方式是完全不同的,powershell 基于完全的面向?qū)ο?,它通過給函數(shù)和對象“起別名”的方式來支持這些舊的命令
具體二者的區(qū)別感興趣的可以去深究,看各位需求。
執(zhí)行命令很簡單,直接用已生成的shell對象去調(diào)用對應的方法就好
res = shell.run_ps('ipconfig')
res = shell.run_cmd('ipconfig')
想要獲取命令返回的內(nèi)容,可以直接
res.std_out.decode()
res還有其他參數(shù),如狀態(tài)碼等,需要的可以直接使用
3.返回結果出現(xiàn)中文亂碼問題
方法一:
首先需明確是不是出現(xiàn)在回顯回來時編碼出現(xiàn)問題
import chardet
print(chartdet.detect(res.std_out))
查看編碼類型
直接decode print出來的編碼類型,例如print出來的是gb2312 ,那么就使用
res.std_out.decode('gb2112')
方法二:
如果不是輸出的時候有問題,那就要考慮發(fā)送命令的時候是不是有問題
首先,應該確保出問題的命令是否能跑,可以通過遠程連接到對應的服務器,手動跑一下,powershell 遠程登錄可以參考:https://blog.csdn.net/weixin_40943540/article/details/89344265
如果確定命令正常,可能是源碼的問題了。
跑到wirm的源碼里看看情況,我這里的版本是pywinrm 0.4.2
def run_cmd(self, command, args=()):
# TODO optimize perf. Do not call open/close shell every time
shell_id = self.protocol.open_shell()
command_id = self.protocol.run_command(shell_id, command, args)
rs = Response(self.protocol.get_command_output(shell_id, command_id))
self.protocol.cleanup_command(shell_id, command_id)
self.protocol.close_shell(shell_id)
return rs
def run_ps(self, script):
"""base64 encodes a Powershell script and executes the powershell
encoded script command
"""
# must use utf16 little endian on windows
encoded_ps = b64encode(script.encode('utf_16_le')).decode('ascii')
rs = self.run_cmd('powershell -encodedcommand {0}'.format(encoded_ps))
if len(rs.std_err):
# if there was an error message, clean it it up and make it human
# readable
rs.std_err = self._clean_error_msg(rs.std_err)
return rs
重點觀察這兩個函數(shù),我們可以看到,當你執(zhí)行 run_ps時,他會通過轉碼后直接調(diào)用run_cmd,所以這是不是可以認為ps命令通過轉碼后就可以跑到cmd執(zhí)行呢?這個只是一個猜測,有待研究。出現(xiàn)中文問題亂碼的問題可能也是出現(xiàn)在這個轉碼上!
通過debug可以看到,我們輸入的命令的condepage為936,而庫默認的編碼為437,這就會使輸入的命令出現(xiàn)亂碼現(xiàn)象,現(xiàn)在要做的就是統(tǒng)一編碼。
第一步:繼承winrm.Session這個類,并進行重寫run_cmd
def run_cmd(self, command, args=()):
# TODO optimize perf. Do not call open/close shell every time
shell_id = self.protocol.open_shell(codepage=936)
command_id = self.protocol.run_command(shell_id, command, args)
rs = winrm.Response(self.protocol.get_command_output(shell_id, command_id))
self.protocol.cleanup_command(shell_id, command_id)
self.protocol.close_shell(shell_id)
return rs
第二步:在執(zhí)行命令前設置好shell的編碼
shell.protocol.open_shell(codepage='936')
重寫調(diào)用run_ps和run_cnd時注意調(diào)用你改寫的類的方法,不然重寫就沒意義了,這樣就可以保證輸入命令時,編碼統(tǒng)一。