使用paramiko.SSHClient的exec_command无法找到可执行文件,但invoke_shell的send可正常执行,原因何在?
这个问题非常典型,根源在于交互式Shell与非交互式Shell的环境变量差异,尤其在z/OS USS这类UNIX兼容环境下表现得尤为明显,和你的代码关系不大,主要是USS环境的Shell加载逻辑导致的:
一、为什么两种方式表现不同?
1. invoke_shell().send()能成功的原因
invoke_shell会启动一个交互式登录Shell,它会自动加载用户的Shell配置文件(比如$HOME/.profile、/etc/profile,或者USS环境特有的配置脚本)。这些配置文件里通常会配置PATH环境变量,将Python的安装目录添加进去,因此Shell能找到python可执行文件。
2. exec_command()失败的原因
exec_command默认启动的是非交互式、非登录Shell,它不会自动加载用户的Profile配置,因此PATH变量是系统默认的极简路径——这个路径里并没有包含Python的安装目录,导致系统找不到python命令,抛出FSUM7351 not found错误。
你测试的cd {remote_script_path}; ls -l能成功,是因为cd是Shell内置命令,ls则在系统默认的PATH中,不需要额外的环境变量配置。
二、如何验证这个猜想?
你可以分别用两种方式打印PATH变量,对比差异:
- 用
invoke_shell打印PATH:shell = client.invoke_shell() shell.send("echo $PATH\n") # 读取并打印输出,查看是否包含Python的安装目录 - 用
exec_command打印PATH:stdin, stdout, stderr = client.exec_command("echo $PATH") print(stdout.read().decode())
你会发现exec_command输出的PATH要短很多,完全没有Python的路径。
三、解决方案(针对z/OS USS环境)
根据你的场景,推荐以下几种可行方案:
方案1:使用Python的绝对路径调用
先通过USS终端或invoke_shell执行which python,获取Python的绝对路径(比如z/OS上常见的路径是/usr/lpp/IBM/cyp/v3r12/pyz/bin/python),然后在exec_command中直接使用绝对路径:
# 替换为你实际的Python绝对路径 python_abs_path = "/usr/lpp/IBM/cyp/v3r12/pyz/bin/python" stdin, stdout, stderr = client.exec_command(f"{python_abs_path} {remote_script_path}/{script}")
方案2:在命令中手动加载Profile配置
在exec_command的命令前,先手动加载用户的Profile脚本,让Shell获取完整的环境变量:
# 假设使用的是Bash Shell,加载$HOME/.profile cmd = f". $HOME/.profile; python {remote_script_path}/{script}" stdin, stdout, stderr = client.exec_command(cmd)
如果是其他Shell(比如zsh),替换为对应的配置文件(如$HOME/.zshrc)。
方案3:显式指定PATH变量
如果知道Python的安装目录,直接在命令中临时扩展PATH:
# 替换为Python所在的实际目录 python_dir = "/usr/lpp/IBM/cyp/v3r12/pyz/bin" cmd = f"export PATH={python_dir}:$PATH; python {remote_script_path}/{script}" stdin, stdout, stderr = client.exec_command(cmd)
四、额外说明
这个问题和安全限制无关——因为你已经能通过exec_command执行cd、ls等命令,核心是环境变量的加载逻辑差异。z/OS USS上的Python(通常是IBM Open Enterprise Python for z/OS)默认不会把路径加入系统全局PATH,必须通过用户Profile或手动指定才能访问。
内容来源于stack exchange




