我正在使用paramiko在远程主机上收集一些信息,并在read()
频道中阅读(readline()
/ readlines()
/ stderr
)时遇到问题。
有时stderr.read()
会返回一个空字符串,这对我来说就像是竞争条件的结果。
但是,根据我在互联网上找到的文档和示例,这似乎是确切的方法。
我还尝试打开专用频道并使用chan.recv_ready()
/ chan.recv_stderr_ready()
并通过chan.recv()
/ chan.recv_stderr()
从循环中读取相应的频道同样的行为。
这是一个最小的测试用例 - 在我的设置中 - 可靠地导致该行为。
import paramiko
class SSH:
def __init__(self):
self.ssh = paramiko.SSHClient()
self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.ssh.connect('127.0.0.1', port=31337, username='root', password='root')
self.stdout = ''
self.stderr = ''
self.exit_code = 0
def _run_cmd(self, cmd):
self.stdout = ''
self.stderr = ''
stdin, stdout, stderr = self.ssh.exec_command(cmd)
self.stdout = stdout.read()
self.stderr = stderr.read()
while not stdout.channel.exit_status_ready():
pass
self.exit_code = stdout.channel.recv_exit_status()
if self.exit_code:
print("ERROR: " + self.stderr)
def process_list(self):
self._run_cmd('ls /proc/ | grep -E "^[0-9]+$" | grep -v $$')
lines = self.stdout.split('\n')[:-1]
data = []
for process in lines:
process_data = {}
process_data['pid'] = int(process)
# fetching and parsing process status information from /proc/[PID]/status
self._run_cmd('cat /proc/%d/status' % (int(process)))
data.append(self.stdout)
return data
data = SSH()
while True:
print data.process_list()
经过几次运行(如果不是第一次),我得到的是:
ERROR:
虽然我期待:
ERROR: cat: /proc/12883/status: No such file or directory
如何确保stderr准备好读取/我读取stderr上的所有数据?
答案 0 :(得分:0)
长话短说:我遇到了大部分问题,并为我的大部分ssh相关问题提出了最终解决方案。因此,请随时查看exec_command
exec_command()
的实现,避免大多数empty_response / stoping场景。
长篇故事
这里的主要问题是你exit_status
是非阻塞的,并产生一个负责频道通信的线程。此线程正在等待传入数据并将其放入通道缓冲区,而主线程可能会继续。这就是你的问题所在。即使在您检查到您的身边已收到exit_status
之前,您也要过早地阅读缓冲区。接收status_code
确认远程进程已退出给定的状态代码。接收远程状态代码不表示您已收到可能仍在传输的所有数据。它甚至可能在您身边无序到达,并表示即使在收到所有数据(stderr
,stdout
)之前,stdout
也可能会到达。
当数据到达时,主线程继续。在您的情况下,主线程尝试从stderr
和exit_status
读取一次然后阻塞,直到exit_status
准备就绪。请注意,通道线程仍可能从远程命令调用接收数据。另请注意,一旦您收到远程execute_command
,您必须手动清空缓冲区。
在您的具体情况下,会发生以下情况:
stdout
产生一个新线程(让我们调用id exec_thread ,管理远程命令调用stderr
,self.stdout
中存储self.stderr
,stderr
个缓冲区的当前内容。请注意,您不知道自己是否已收到stdout
或stdout
的所有数据。很可能你刚收到一些块。stderr
,exit_status
的数据。stderr
。请注意,此时您的缓冲区可能仍然已满,最近已收到stdout
,exit_status
块。self.exit_code
存储在run_cmd
中。 [exec_thread]仍然存在,可能仍会收到一些乱序数据。输入缓冲区可能仍会被填充。exec_command
请注意,该频道的[exec_thread] - paramiko为每个频道调用创建一个主题(即stdout.channel.close()
) - 仍处于活动状态,并且他们会总结空闲并创建问题,直到您手动关闭它们( INSERT INTO Tbl_Basicinfo(Employer_Id,Lname,Fname,UserName,Userpass,Location_Id,cc2,CC3,cc4,cc5,Dohire,Job_Status,ssn,Import_Emp_No,[Benefit_Terminate], [Show_Age], [Change_IP], [IsNewHired])
SELECT M.Employer_Id,M.LASTNAME,M.FIRSTNAME,UPPER(LEFT(ISNULL(M.FIRSTNAME,'A'),1)+''+LEFT(ISNULL(M.LASTNAME,'C'),1))+CONVERT(VARCHAR,ISNULL(MaxImportEENumber,0)+ISNULL(ROWNUM,0)) [UserName],RIGHT(NEWID(),10) [UserPass],ISNULL(M.Location_Id,0)[Location_Id],M.Level2code,M.Level3code,M.LEVEL4Code,M.LEVEL5Code,M.DATEOFHIRE,
1 [JobStatus],SSN,M.MaxImportEENumber+ROWNUM [ImportEmpNo],0 [Benefit_Terminate], 0[Show_Age],M.ChangeIP,1 [IsNewHired] from(
SELECT ROW_NUMBER() OVER(ORDER BY [FIRSTNAME]) AS ROWNUM,* FROM
(
SELECT DISTINCT @Employer_Id [Employer_Id],PR.LASTNAME,PR.FIRSTNAME,(SELECT MAX(CAST(ISNULL(Employee_Id,0) as BIGINT)) from Tbl_Basicinfo) [MaxImportEENumber],
(SELECT TOP 1 Location_Id from Tbl_Location where Location_Code=PR.LEVEL1CODE and Employer_Id=@Employer_Id and Location like '%'+PR.LEVEL1DESCRIPTION+'%') [Location_Id],
(SELECT TOp 1 LEVEL2Code from Tbl_PPACA_Import where ssn=PR.SSN and EmployerId=PR.EmployerId) LEVEL2Code,
(SELECT TOp 1 LEVEL3Code from Tbl_PPACA_Import where ssn=PR.SSN and EmployerId=PR.EmployerId) LEVEL3Code,
(SELECT TOp 1 LEVEL4Code from Tbl_PPACA_Import where ssn=PR.SSN and EmployerId=PR.EmployerId) LEVEL4Code,
(SELECT TOp 1 LEVEL5Code from Tbl_PPACA_Import where ssn=PR.SSN and EmployerId=PR.EmployerId) LEVEL5Code,
(SELECT MIN(DATEOFHIRE) from Tbl_PPACA_Import where ssn=PR.SSN and EmployerId=PR.EmployerId) DATEOFHIRE,1 Job_Status,PR.SSN,PR.ChangeIP
from Tbl_PPACA_Import PR
LEFT OUTER JOIN Tbl_Basicinfo L ON L.ssn=PR.SSN and L.Employer_Id=PR.EmployerId
WHERE PR.EmployerId=@Employer_Id and L.SSN is null and ISNULL(PR.SSN,'') not like '' AND ISNUMERIC(PR.SSN)=1
and ISNULL(PR.PPE,DATEOFHIRE) =(SELECT MAX(ISNULL(PPE,DATEOFHIRE)) FROM Tbl_PPACA_Import I WHERE I.SSN=PR.SSN AND I.EmployerId=PR.EmployerId )
) V
) M;
)。