杰瑞科技汇

Python如何远程执行另一台机器的脚本?

方法概览

方法 核心技术 优点 缺点 适用场景
SSH paramiko Python 原生,无需额外服务,功能强大 需要处理密钥认证,代码相对复杂 需要精细控制,安全的自动化任务
HTTP API Flask/FastAPI + requests 架构灵活,可扩展性好,易于集成到Web服务 需要搭建和维护一个HTTP服务 微服务架构,需要与Web应用交互的场景
消息队列 Celery + RabbitMQ/Redis 异步执行,高可用,可分布式,任务队列强大 架构复杂,需要部署多个组件 后台异步任务,耗时的分布式计算
Ansible ansible 模块 无需在远程主机安装Agent,声明式语法,幂等性 学习曲线,需要管理清单和Playbook 大规模服务器配置管理和批量操作

使用 Paramiko (SSH 的 Python 实现)

这是最直接、最常用的方法之一,Paramiko 是一个纯 Python 实现的 SSHv2 协议库,它让你可以用 Python 代码来执行远程命令、传输文件等。

Python如何远程执行另一台机器的脚本?-图1
(图片来源网络,侵删)

安装 Paramiko

pip install paramiko

准备工作

  • 你需要知道远程主机的 IP 地址用户名密码SSH 密钥
  • 为了安全,强烈推荐使用 SSH 密钥认证 而非密码。

代码示例

场景 A:使用密码认证

假设你要在远程主机上执行一个简单的命令 ls -l

import paramiko
import os
# --- 远程服务器信息 ---
hostname = 'your_server_ip'
port = 22
username = 'your_username'
password = 'your_password' # 为了安全,建议使用环境变量或密钥管理服务
# --- 要执行的命令 ---
# 你可以直接在这里写命令,也可以执行一个远程脚本文件
command_to_run = 'ls -l /home'
script_to_run = 'python /path/to/remote_script.py'
# --- 创建SSH客户端 ---
try:
    # 1. 创建SSHClient对象
    ssh_client = paramiko.SSHClient()
    # 2. 自动添加不在known_hosts文件中的主机名(仅用于测试,生产环境不推荐)
    ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    # 3. 连接服务器
    ssh_client.connect(hostname, port=port, username=username, password=password)
    # 4. 执行命令
    print(f"正在执行命令: {command_to_run}")
    stdin, stdout, stderr = ssh_client.exec_command(command_to_run)
    # 5. 获取命令的输出
    exit_status = stdout.channel.recv_exit_status() # 获取命令退出状态码
    output = stdout.read().decode('utf-8')
    error = stderr.read().decode('utf-8')
    if exit_status == 0:
        print("命令执行成功!")
        print("输出:\n", output)
    else:
        print(f"命令执行失败,状态码: {exit_status}")
        print("错误信息:\n", error)
    # 6. 关闭连接
    ssh_client.close()
except paramiko.AuthenticationException:
    print("认证失败,请检查用户名和密码/密钥。")
except paramiko.SSHException as e:
    print(f"SSH连接或命令执行出错: {e}")
except Exception as e:
    print(f"发生未知错误: {e}")

场景 B:使用 SSH 密钥认证(更安全)

将上面的 ssh_client.connect 部分替换为:

Python如何远程执行另一台机器的脚本?-图2
(图片来源网络,侵删)
# --- 密钥认证 ---
private_key_path = os.path.expanduser('~/.ssh/id_rsa') # 默认私钥路径
# 或者指定你的私钥文件路径
# private_key_path = '/path/to/your/private_key'
try:
    ssh_client = paramiko.SSHClient()
    ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    # 使用私钥文件进行连接
    private_key = paramiko.RSAKey.from_private_key_file(private_key_path)
    ssh_client.connect(hostname, port=port, username=username, pkey=private_key)
    # ... 后续执行命令的代码相同 ...
except paramiko.AuthenticationException:
    print("认证失败,请检查密钥是否正确,并已添加到远程服务器的authorized_keys中。")
# ... except 块相同 ...

场景 C:执行远程脚本文件

假设远程服务器 /home/user/ 目录下有一个 remote_script.py 文件,内容如下:

# remote_script.py
import time
import random
print("远程脚本开始执行...")
time.sleep(2)
print(f"这是一个来自远程脚本的问候!随机数是: {random.randint(1, 100)}")
print("远程脚本执行完毕。")

在本地执行这个远程脚本:

# ... 连接代码同上 ...
# 执行远程脚本
# 注意:python 命令需要在远程服务器的 PATH 环境变量中
command = 'python /home/user/remote_script.py'
stdin, stdout, stderr = ssh_client.exec_command(command)
# 获取输出
output = stdout.read().decode()
error = stderr.read().decode()
print("--- 远程脚本输出 ---")
print(output)
if error:
    print("--- 远程脚本错误 ---")
    print(error)
# ... 关闭连接 ...

使用 HTTP API (Web 服务)

这种方法的核心思想是:在远程主机上运行一个微型的 Web 服务器(如 Flask),这个服务器提供一个 API 端点(如 /run_script),本地机器(或其他任何机器)通过向这个 API 发送 HTTP 请求来触发脚本的执行。

远程主机端 (Flask 服务器)

安装 Flask:

pip install Flask

创建 server.py 文件并部署到远程主机上:

# server.py
from flask import Flask, request, jsonify
import subprocess
import os
app = Flask(__name__)
@app.route('/run_script', methods=['POST'])
def run_remote_script():
    # 从请求中获取要执行的脚本路径和参数
    data = request.get_json()
    script_path = data.get('script_path')
    script_args = data.get('args', [])
    if not script_path or not os.path.exists(script_path):
        return jsonify({"error": "Invalid script path"}), 400
    try:
        # 使用subprocess执行脚本,并捕获输出
        # 注意:这种方法有安全风险,如果脚本路径由用户控制,可能导致命令注入
        # 在生产环境中,必须对输入进行严格验证和清理
        command = ['python'] + [script_path] + script_args
        process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout, stderr = process.communicate()
        if process.returncode == 0:
            return jsonify({
                "status": "success",
                "output": stdout.decode('utf-8')
            }), 200
        else:
            return jsonify({
                "status": "error",
                "error_code": process.returncode,
                "error_message": stderr.decode('utf-8')
            }), 500
    except Exception as e:
        return jsonify({"error": str(e)}), 500
if __name__ == '__main__':
    # 在生产环境中,应该使用 Gunicorn 或 uWSGI 来运行
    app.run(host='0.0.0.0', port=5000)

本地端 (请求客户端)

创建 client.py 文件,用来调用远程服务器的 API:

# client.py
import requests
import json
# 远程服务器地址
SERVER_URL = 'http://your_server_ip:5000/run_script'
# 定义要执行的远程脚本路径和参数
payload = {
    "script_path": "/home/user/remote_script.py", # 远程服务器上的脚本路径
    "args": ["--input", "data.txt", "--verbose"]  # 传递给脚本的参数
}
try:
    response = requests.post(SERVER_URL, json=payload)
    response.raise_for_status()  # 如果请求失败 (状态码非 200), 则抛出异常
    result = response.json()
    print("API 响应:")
    print(json.dumps(result, indent=2))
    if result.get('status') == 'success':
        print("\n脚本执行成功!输出:")
        print(result.get('output'))
    else:
        print("\n脚本执行失败!")
        print(f"错误码: {result.get('error_code')}")
        print(f"错误信息: {result.get('error_message')}")
except requests.exceptions.RequestException as e:
    print(f"请求失败: {e}")

优点:

  • 架构非常灵活,可以轻松集成到现有的 Web 应用中。
  • 可以添加认证(如 API Key, JWT)、日志、限流等中间件。

缺点:

  • 需要在远程主机上持续运行一个 Web 服务。
  • 安全性需要特别注意,要防止未授权的访问和恶意代码执行。

如何选择?

  • 如果你只是想快速、安全地在一台或几台服务器上执行一些命令或脚本,并且你熟悉 SSH,使用 Paramiko 是最佳选择,它轻量、直接且功能强大。
  • 如果你的远程执行功能是某个更大 Web 应用或自动化流程的一部分,并且你需要提供 API 接口给其他服务调用,使用 HTTP API 的方式更合适
  • 如果你需要处理大量、异步、可重试的后台任务,比如数据处理、邮件发送等,考虑使用 Celery 这样的消息队列系统
  • 如果你的目标是配置管理、部署应用或批量执行一系列标准化的操作,而不是执行一个特定的、已存在的脚本,Ansible 可能是更专业的工具

对于大多数 Python Paramiko 是最常用和最容易上手的远程执行方案。

分享:
扫描分享到社交APP
上一篇
下一篇