我可以在Python中捕获远程执行的命令及其输出到日志文件中吗?

1 投票
2 回答
12618 浏览
提问于 2025-04-17 06:06

我正在用paramiko在Python中执行远程命令。我想把完整的远程命令行细节记录到日志文件里。

比如,像在perl中使用Expect那样,我们可以把下面的命令和它的输出记录到日志文件中。

samir@ssahoo-ub-in:~$ cat logfile.txt
samir@ssahoo-ub-in:~$ ls
Desktop  Documents  Downloads  examples.desktop  getskype-linux-beta-ubuntu-64  Music      Pictures  Public  Templates  Videos  VirtualBox VMs
samir@ssahoo-ub-in:~$ hostname
ssahoo-ub-in
samir@ssahoo-ub-in:~$ w
 09:11:12 up 10:26,  6 users,  load average: 0.05, 0.05, 0.06
USER     TTY      FROM              LOGIN@   IDLE   JCPU   PCPU WHAT
samir    tty7     :0               22:45   10:26m 12:51   0.23s gnome-session     --session=classic-gnome
samir    pts/0    :0.0             22:46   10:24m 32.80s 14.34s gnome-terminal
samir    pts/1    :0.0             23:49    1:03m  0.57s  0.57s bash
samir    pts/2    :0.0             07:42    1:22m  0.48s  0.09s vim ../projects    /test/test_cases/common/TestHostname.py
samir    pts/3    :0.0             08:11    0.00s  0.57s  0.01s w
samir    pts/4    :0.0             08:24   46:08   0.21s  0.04s python
samir@ssahoo-ub-in:~$ who
samir    tty7         2011-11-10 22:45 (:0)
samir    pts/0        2011-11-10 22:46 (:0.0)
samir    pts/1        2011-11-10 23:49 (:0.0)
samir    pts/2        2011-11-11 07:42 (:0.0)
samir    pts/3        2011-11-11 08:11 (:0.0)
samir    pts/4        2011-11-11 08:24 (:0.0)
samir@ssahoo-ub-in:~$ 

我通过使用paramiko.invoke_shell()解决了上面的问题。

client = paramiko.SSHClient()
client.load_system_host_keys()
client.load_host_keys(os.path.expanduser('~/.ssh/known_hosts'))
client.set_missing_host_key_policy(AllowAllKeys())
client.connect(HOST, username=USER, password=PASSWORD)

channel = client.invoke_shell()
channel.send("ls -l\n")
while not channel.recv_ready():
    time.sleep(2)
results += channel.recv(1024)

但是,有人能帮我获取标准输出(stdout)、标准错误(stderr)和返回代码(退出状态)吗?

我在调用shell后尝试使用recv_stderr和recv_exit_status。但是当我尝试打印stderr和退出状态时,它什么也不打印。以下是我的代码:

import paramiko, time
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 
ssh.connect('localhost', username='sam', password='mypassword')
channel = ssh.invoke_shell()
results = ''
results2 = ''
password = 'my_root_password'
try:
    channel.send("su -\n")
    while not channel.recv_ready():
        print "Waiting for root challenge..."
        time.sleep(2)
    results += channel.recv(1024)
    channel.send("%s\n" % password)
    while not channel.recv_ready():
        print "Authenticating..."
        time.sleep(2)
    results += channel.recv(1024)

    channel.send("ls file_doesnt_exist\n") # Here I am sending a wrong command to fail
    if channel.exit_status_ready():
        result3 = channel.recv_exit_status(1024)
    print "exit status is:", results3            # It doesnt return anything
    if channel.recv_stderr_ready():
        result2 = channel.recv_stderr(1024)
    print results2                                # Doesn't print error

except Exception, e:
    print e

我在返回代码上遇到了一些问题。可能是我没有正确使用。每次我打印返回代码时,它都和第一次返回的一样。难道我需要重置返回代码吗?在下面的例子中,我对两个命令的返回代码都是2。现在当我交换这两个命令,也就是把'ls -al;exit\n'替换成'ls -al file_not_exist;exit\n',反之亦然,它却打印返回代码0。每次它都打印和第一次返回的一样的返回代码。

channel = client.invoke_shell()
channel.send('ls -al file_not_exist;exit\n') #Sending to list a file which doesn't exist
time.sleep(3)
print "My 1st command exit status is: ",channel.exit_status_ready()
print "My 1st command return code is: ", channel.recv_exit_status()

channel.send('ls -al;exit\n')
time.sleep(3)
print "My 2nd command exit status is: ",channel.exit_status_ready()
print "My 2nd command return code is: ",channel.recv_exit_status()

我需要打印每个命令的返回代码。你能帮我解决这个问题吗?

2 个回答

0

下面是如何使用fabric(这是一个在paramiko等基础上构建的高级工具)来捕获远程执行命令的输出:

#file: fabfile.py
import functools
import logging
import sys

from fabric.api import run, hide

def do_stuff():
    for cmd in ['ls', 'w', 'who']:
        run(cmd)

# configure logging
logger = logging.getLogger("logged")
logger.setLevel(logging.INFO)
logger.addHandler(logging.FileHandler('logfile.txt'))

def logged(func):
    """Logging decorator."""
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        with hide('output'):
            output = func(*args, **kwargs)
        logger.info(output)
        return output
    return wrapper
run = logged(run)

示例

$ fab do_stuff -H localhost
[localhost] Executing task 'do_stuff'
[localhost] run: ls
[localhost] run: w
[localhost] run: who

Done.
Disconnecting from localhost... done.

logfile.txt文件中包含了lswwho命令的输出结果。

3

看起来根据 paramiko.SSHClient 的文档,你可以使用 recv_ready 和 recv 来获取 shell 或通道的结果。例如,这段代码对我有效:

client = SSHClient()
lient.load_system_host_keys()
client.set_missing_host_key_policy(AllowAllKeys())
client.connect(HOST,username=USERNAME,password=PASSWORD)
channel = client.invoke_shell()
channel.send('ls\n')
while channel.recv_ready():
    channel.recv(1024)

channel.send('exit\n')
if channel.exit_status_ready():
    print channel.recv_exit_status()

撰写回答