【问题标题】:How do you execute multiple commands in a single session in Paramiko? (Python)如何在 Paramiko 的单个会话中执行多个命令? (Python)
【发布时间】:2011-06-01 15:25:03
【问题描述】:
def exec_command(self, command, bufsize=-1):
    #print "Executing Command: "+command
    chan = self._transport.open_session()
    chan.exec_command(command)
    stdin = chan.makefile('wb', bufsize)
    stdout = chan.makefile('rb', bufsize)
    stderr = chan.makefile_stderr('rb', bufsize)
    return stdin, stdout, stderr

在 paramiko 中执行命令时,它总是会在您运行 exec_command 时重置会话。 当我运行另一个 exec_command 时,我希望能够执行 sudo 或 su 并且仍然拥有这些权限。 另一个示例是尝试 exec_command("cd /") 然后再次运行 exec_command 并将其放在根目录中。我知道您可以执行类似 exec_command("cd /; ls -l") 的操作,但我需要在单独的函数调用中执行此操作。

【问题讨论】:

标签: python paramiko


【解决方案1】:

非交互式用例

这是一个非交互式示例...它发送cd tmpls,然后是exit

import sys
sys.stderr = open('/dev/null')       # Silence silly warnings from paramiko
import paramiko as pm
sys.stderr = sys.__stderr__
import os

class AllowAllKeys(pm.MissingHostKeyPolicy):
    def missing_host_key(self, client, hostname, key):
        return

HOST = '127.0.0.1'
USER = ''
PASSWORD = ''

client = pm.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()
stdin = channel.makefile('wb')
stdout = channel.makefile('rb')

stdin.write('''
cd tmp
ls
exit
''')
print stdout.read()

stdout.close()
stdin.close()
client.close()

交互式用例
如果您有交互式用例,此答案将无济于事……我个人会使用 pexpectexscript 进行交互式会话。

【讨论】:

  • 但是这个解决方案不允许在所有命令完成之前读取第一个命令的输出。我说的对吗?
  • 这不起作用,因为 stdout.read() 读取整个文件。意思是,它读取“键入”到终端的程序。这不是预期的行为。如何只读取ls的输出而不是整个程序和ls的输出?
  • 我和上面的两个 cmets... 有什么方法可以在所有命令完成之前读取一个命令的输出?
  • @Mike Pennington 嗨,伙计!谢谢你的回答,它实际上工作得很好,但是,sudo 命令有一些问题,你能看看我的问题吗? *.com/questions/34068218/…
  • @MatthewMoisen:你应该使用 exit 命令作为最后一个命令。
【解决方案2】:

尝试创建一个以\n 字符分隔的命令字符串。它对我有用。 为了。例如ssh.exec_command("command_1 \n command_2 \n command_3")

【讨论】:

    【解决方案3】:

    严格来说,你不能。根据 ssh 规范:

    会话是程序的远程执行。该程序可能是一个 shell、应用程序、系统命令或某些内置子系统。

    这意味着,一旦命令执行完毕,会话就结束了。您不能在一个会话中执行多个命令。但是,您可以做的是启动一个远程 shell(== 一个命令),并通过 stdin 等与该 shell 交互......(想想执行 python 脚本与运行交互式解释器)

    【讨论】:

    • SSH RFC 没有说明会话是否应该在执行命令后立即终止。如果您看过大多数 ssh 客户端,他们会在会话建立后继续打开 Exec/Shell。允许用户键入任何数字命令。当用户键入“exit”时,只会终止会话。
    【解决方案4】:

    您可以通过在客户端调用 shell 并发送命令来做到这一点。请参考here
    该页面包含 python 3.5 的代码。我对代码进行了一些修改以适用于 pythin 2.7。在此处添加代码以供参考

    import threading, paramiko
    
    strdata=''
    fulldata=''
    
    class ssh:
        shell = None
        client = None
        transport = None
    
        def __init__(self, address, username, password):
            print("Connecting to server on ip", str(address) + ".")
            self.client = paramiko.client.SSHClient()
            self.client.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
            self.client.connect(address, username=username, password=password, look_for_keys=False)
            self.transport = paramiko.Transport((address, 22))
            self.transport.connect(username=username, password=password)
    
            thread = threading.Thread(target=self.process)
            thread.daemon = True
            thread.start()
    
        def close_connection(self):
            if(self.client != None):
                self.client.close()
                self.transport.close()
    
        def open_shell(self):
            self.shell = self.client.invoke_shell()
    
        def send_shell(self, command):
            if(self.shell):
                self.shell.send(command + "\n")
            else:
                print("Shell not opened.")
    
        def process(self):
            global strdata, fulldata
            while True:
                # Print data when available
                if self.shell is not None and self.shell.recv_ready():
                    alldata = self.shell.recv(1024)
                    while self.shell.recv_ready():
                        alldata += self.shell.recv(1024)
                    strdata = strdata + str(alldata)
                    fulldata = fulldata + str(alldata)
                    strdata = self.print_lines(strdata) # print all received data except last line
    
        def print_lines(self, data):
            last_line = data
            if '\n' in data:
                lines = data.splitlines()
                for i in range(0, len(lines)-1):
                    print(lines[i])
                last_line = lines[len(lines) - 1]
                if data.endswith('\n'):
                    print(last_line)
                    last_line = ''
            return last_line
    
    
    sshUsername = "SSH USERNAME"
    sshPassword = "SSH PASSWORD"
    sshServer = "SSH SERVER ADDRESS"
    
    
    connection = ssh(sshServer, sshUsername, sshPassword)
    connection.open_shell()
    connection.send_shell('cmd1')
    connection.send_shell('cmd2')
    connection.send_shell('cmd3')
    time.sleep(10)
    print(strdata)    # print the last line of received data
    print('==========================')
    print(fulldata)   # This contains the complete data received.
    print('==========================')
    connection.close_connection()
    

    【讨论】:

    • 您的意思是键入 sendShell,而不是 send_shell?还是 send_shell 您正在调用的 paramiko.ssh 类的内置函数?
    • @Fields,不错。我已经更新了代码。我忘记在 ssh 类中将 sendShell 重命名为 send_shell。谢谢!
    【解决方案5】:
    cmd = 'ls /home/dir'
    self.ssh_stdin, self.ssh_stdout, self.ssh_stderr = self.ssh.exec_command(cmd)
    print self.ssh_stdout.read()
    cmd2 = 'cat /home/dir/test.log'
    self.ssh_stdin2, self.ssh_stdout2, self.ssh_stderr2 = self.ssh.exec_command(cmd2)
    print self.ssh_stdout2.read()
    

    【讨论】:

      【解决方案6】:

      您可以使用以下技术运行多个命令。使用分号分隔 Linux 命令 例如:

      chan.exec_command("date;ls;free -m")
      

      【讨论】:

        【解决方案7】:

        您可以执行整个 BASH 脚本文件以更好地使用,这里是代码:

        import paramiko
        
        hostname = "192.168.1.101"
        username = "test"
        password = "abc123"
        
        # initialize the SSH client
        client = paramiko.SSHClient()
        # add to known hosts
        client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        try:
            client.connect(hostname=hostname, username=username, password=password)
        except:
            print("[!] Cannot connect to the SSH Server")
            exit()
        
        # read the BASH script content from the file
        bash_script = open("script.sh").read()
        # execute the BASH script
        stdin, stdout, stderr = client.exec_command(bash_script)
        # read the standard output and print it
        print(stdout.read().decode())
        # print errors if there are any
        err = stderr.read().decode()
        if err:
            print(err)
        # close the connection
        client.close()
        

        这将在远程192.168.1.101 Linux 机器上执行本地script.sh 文件。

        script.sh(只是一个例子):

        cd Desktop
        mkdir test_folder
        cd test_folder
        echo "$PATH" > path.txt
        

        本教程详细解释了这一点:How to Execute BASH Commands in a Remote Machine in Python

        【讨论】:

        • 这不是真正的 bash 脚本。您正在 用户的默认 shell 中将文件的内容作为命令执行。
        • 另外,通过这种方式使用AutoAddPolicy,您将失去对 MITM 攻击的保护。
        • @MartinPrikryl 你能指出读取文件内容和执行 BASH 脚本之间的区别吗?
        • 我不明白您的评论与我的评论有何关系。我是说您不是在 bash 中执行命令,而是在用户的 默认 shell(可以是任何其他 shell)中执行命令。
        • 您的代码将盲目接受任何主机密钥,更糟糕的是任何更改主机密钥。所以你的代码容易受到MITM attacks的攻击。
        【解决方案8】:

        如果您希望每个命令对您应该使用的下一个命令产生影响:

        stdin, stdout, stderr = client.exec_command("command1;command2;command3")
        

        但在某些情况下,我发现当“;”不起作用,使用“&&”确实有效。

        stdin, stdout, stderr = client.exec_command("command1 && command2 && command3")
        

        【讨论】:

          最近更新 更多