子进程 Popen 无法发送多个命令,命令似乎被截断

1 投票
1 回答
50 浏览
提问于 2025-04-14 16:20

假设有以下的Python代码,我想打开一个SSH连接,并保持这个连接打开,以便发送多个命令。我知道communicate这个方法不行,因为它会关闭连接,所以我在用write,但是write似乎会把命令之间的空格给切掉。

class ReadOutputThread(threading.Thread):
    def __init__(self, file_obj, output_queue):
        super().__init__()
        self.file_obj = file_obj
        self.output_queue = output_queue
        self.daemon = True

    def run(self):
        for line in iter(self.file_obj.readline, ''):
            self.output_queue.put(line.strip())
        self.file_obj.close()

class YourClass:
    def __init__(self, username: str, ssh_key: str, hostname: str="1.1.1.1"):
        self.ssh_session = subprocess.Popen(['ssh', '-T', '-i', ssh_key, '-Y', f'{username}@{hostname}'],
                                            stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                                            universal_newlines=True, shell=False)
        self.output_queue = queue.Queue()
        self.stdout_thread = ReadOutputThread(self.ssh_session.stdout, self.output_queue)
        self.stderr_thread = ReadOutputThread(self.ssh_session.stderr, self.output_queue)
        self.stdout_thread.start()
        self.stderr_thread.start()
        self.execute_ssh_cmd(" ") # clean 
        self.ssh_session.stdin.flush()

    def execute_ssh_cmd(self, command):
        try:
            # Debug: Print the command before sending
            print(f"Executing command: {command}")

            # Send the command to the SSH session
            self.ssh_session.stdin.write(command)
            self.ssh_session.stdin.write(" \n")
            self.ssh_session.stdin.flush()  # Ensure data is sent immediately

            # Read the output from the queue
            output_lines = []
            while True:
                try:
                    line = self.output_queue.get(timeout=1)
                    if command.strip() in line.strip():  # Check if the command is part of the line
                        continue
                    output_lines.append(line)
                except queue.Empty:
                    break

            return output_lines
        except Exception as e:
            print("Error writing to SSH session:", e)
            return None

主要代码块:

ssh_key_path = ""
hostname = "1.1.1.1"
username = 'user'

your_instance = YourClass(username, ssh_key_path, hostname)
output1 = your_instance.execute_ssh_cmd('ls -l')
print("Final output 1:", output1)

output2 = your_instance.execute_ssh_cmd("pwd")
print("Final output 2:", output2)

输出结果:

Executing command: ls
Final output 1: [': No such file or directory']
Executing command: pwd
Final output 2: ['/root']

你可以看到,ls -l这个命令被切掉了。

我尝试用带空格的命令重新输入,比如用""、''、f字符串格式和其他字符串选项。

1 个回答

1

与其使用 subprocess,不如试试一个叫做 Paramiko 的模块。你可以通过 pip3 install paramiko 来安装它。这个模块专门用于在 Python 中处理 SSH 连接。而且使用这个模块非常简单。你需要把多个命令放在一个列表里,然后可以逐个执行这些命令。

import paramiko as pm

# Create SSH instance
ssh = pm.SSHClient()

# Automatically add host keys without prompting
ssh.set_missing_host_key_policy(pm.AutoAddPolicy())

# Connect to the SSH server
ssh.connect(hostname='<hostname>', username='<username>', password='<password>')

# keep the connection ON and run multiple commands
try:
    # Open a channel
    ch = ssh.invoke_shell()

    # create a list containg your commands
    cmds = ['cmd1', 'cmd2', 'cmd3']
    for x in cmds:
        # Send the command
        ch.send(x + '\n')

        # Wait for the command to finish 
        while not ch.recv_ready(): pass
            

        # Read and print the command output
        output = ch.recv(1024).decode()
        print(output)

    # Close the channel
    ch.close()
    
except: print("Error writing to SSH session:")

# Close the SSH connection
finally: ssh.close()
    
    

撰写回答