运行命令和本地或通过SSH操作文件,使用相同的界面
项目描述
在本地运行echo
import spur
shell = spur.LocalShell()
result = shell.run(["echo", "-n", "hello"])
print(result.output) # prints hello
通过SSH执行相同的命令使用相同的界面——唯一的区别是shell的创建方式
import spur
shell = spur.SshShell(hostname="localhost", username="bob", password="password1")
with shell:
result = shell.run(["echo", "-n", "hello"])
print(result.output) # prints hello
安装
pip install spur
Shell构造函数
LocalShell
无参数
spur.LocalShell()
SshShell
需要主机名。还需要一些必要的组合,包括用户名、密码和私钥,以进行身份验证
# Use a password
spur.SshShell(
hostname="localhost",
username="bob",
password="password1"
)
# Use a private key
spur.SshShell(
hostname="localhost",
username="bob",
private_key_file="path/to/private.key"
)
# Use a port other than 22
spur.SshShell(
hostname="localhost",
port=50022,
username="bob",
password="password1"
)
可选参数
connect_timeout - 建立SSH连接的超时时间(秒)。默认值为60(一分钟)。
missing_host_key - 默认情况下,当缺少主机密钥时将引发错误。可以使用以下值之一更改缺少主机密钥时的行为
spur.ssh.MissingHostKey.raise_error - 引发错误
spur.ssh.MissingHostKey.warn - 接受主机密钥并记录警告
spur.ssh.MissingHostKey.accept - 接受主机密钥
shell_type – 主机使用的shell类型。默认为 spur.ssh.ShellTypes.sh,适用于大多数Linux发行版。如果主机使用不同的shell,例如在嵌入式系统中常见的简单shell,尝试将 shell_type 更改为更合适的值,例如 spur.ssh.ShellTypes.minimal。目前支持以下shell类型
spur.ssh.ShellTypes.sh – Bourne shell。支持所有功能。
spur.ssh.ShellTypes.minimal – 最小化shell。一些功能不支持
不存在的命令不会引发 spur.NoSuchCommandError。
除非设置为默认值,否则不支持 spawn 和 run 的以下参数: cwd、update_env 和 store_pid。
look_for_private_keys – 默认情况下,Spur会在 ~/.ssh/ 中搜索可发现的私钥文件。设置为 False 以禁用此行为。
load_system_host_keys – 默认情况下,Spur会尝试从用户使用的OpenSSH的已知主机文件中读取主机密钥,如果文件无法读取,则不会引发异常。设置为 False 以禁用此行为。
sock – 用于与目标主机通信的开放套接字或类似套接字的对象。例如
sock=paramiko.proxy.ProxyCommand( "ssh -q -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" "bob@proxy.example.com nc -q0 target.example.com 22" )
类似套接字对象的示例包括
paramiko.proxy.ProxyCommand (自撰写本文时起在Python 3中不受支持)
Shell接口
run(command, cwd, update_env, store_pid, allow_error, stdout, stderr, encoding)
运行一个命令并等待其完成。命令预期为字符串列表。返回一个 ExecutionResult 实例。
result = shell.run(["echo", "-n", "hello"])
print(result.output) # prints hello
注意,参数不进行任何shell扩展。例如,shell.run(["echo", "$PATH"]) 将打印字面字符串 $PATH 而不是环境变量 $PATH 的值。
如果尝试执行不存在的命令,将引发 spur.NoSuchCommandError。
如果更改当前目录到 cwd 失败,将引发 spur.CouldNotChangeDirectoryError。
可选参数
cwd – 在执行命令之前将当前目录更改为此值。
update_env – 一个包含要设置的环环境变量的 dict。如果存在具有相同名称的环境变量,则将其覆盖。否则,保持不变。
store_pid – 当调用 spawn 时,如果设置为 True,则将启动进程的进程ID存储在返回的进程对象上的 pid 属性中。调用 run 时没有影响。
allow_error – 默认为 False。如果为 False,则如果命令的返回码不是0,将引发异常。如果为 True,则无论返回码如何,都会返回结果。
stdout – 如果不是 None,则在命令执行期间打印到标准输出的任何内容也将使用 stdout.write 写入到 stdout。
stderr – 如果不是 None,则在命令执行期间打印到标准错误的任何内容也将使用 stderr.write 写入到 stderr。
编码 – 如果设置,则用于解码任何输出。默认情况下,任何输出都作为原始字节处理。如果设置,则在写入传递的stdout和stderr参数(如果设置)以及设置结果输出属性之前,将解码原始字节。
shell.run(*args, **kwargs)应与shell.spawn(*args, **kwargs).wait_for_result()类似。
spawn(command, cwd, update_env, store_pid, allow_error, stdout, stderr, encoding)
行为与run相同,不同之处在于spawn立即返回表示正在运行进程的对象。
如果尝试执行不存在的命令,将引发 spur.NoSuchCommandError。
如果更改当前目录到 cwd 失败,将引发 spur.CouldNotChangeDirectoryError。
open(path, mode="r")
打开位于path的文件。返回一个文件对象。
默认情况下,文件以文本模式打开。在模式中追加“b”将使文件以二进制模式打开。
例如,要使用SSH复制二进制文件,假设您已经有一个SshShell实例
with ssh_shell.open("/path/to/remote", "rb") as remote_file:
with open("/path/to/local", "wb") as local_file:
shutil.copyfileobj(remote_file, local_file)
close()
关闭shell并释放任何相关资源。当shell用作上下文管理器时,将自动调用close()。
进程接口
由shell.spawn调用返回。具有以下属性
pid – 进程的进程ID。只有在调用spawn时将store_pid设置为True时才可用。
具有以下方法
is_running() – 如果进程仍在运行,则返回True,否则返回False。
stdin_write(value) – 将value写入进程的标准输入。
wait_for_result() – 等待进程退出,然后返回ExecutionResult实例。如果返回码不是零且未调用shell.spawn时未设置allow_error=True,则将引发RunProcessError。
send_signal(signal) – 向进程发送信号signal。只有在调用spawn时将store_pid设置为True时才可用。
类
ExecutionResult
ExecutionResult具有以下属性
return_code – 命令的返回码
output – 包含捕获的stdout结果的字符串
stderr_output – 包含捕获的stderr结果的字符串
它还具有以下方法
to_error() – 返回相应的RunProcessError。如果您想有条件地引发RunProcessError,这很有用,例如
result = shell.run(["some-command"], allow_error=True)
if result.return_code > 4:
raise result.to_error()
RunProcessError
RunProcessError是具有与ExecutionResult相同属性的RuntimeError的子类
return_code – 命令的返回码
output – 包含捕获的stdout结果的字符串
stderr_output – 包含捕获的stderr结果的字符串
NoSuchCommandError
NoSuchCommandError具有以下属性
command – 无法找到的命令
CouldNotChangeDirectoryError
CouldNotChangeDirectoryError具有以下属性
directory – 无法更改到的目录
API稳定性
使用来自语义版本控制的术语,如果spur的版本是X.Y.Z,则X是主版本,Y是次要版本,Z是修补版本。
当主版本为0时,增加修补版本表示向后兼容的更改。例如,如果您正在使用0.3.1,则可以安全地升级到0.3.2。
增加次要版本号表示API发生了变化。这意味着使用spur先前次要版本的任何代码在能够使用当前次要版本之前可能需要更新。
未记录的功能
一些功能未记录,应被视为实验性。请自行承担使用风险。它们可能表现不正确,其行为和接口可能随时更改。
故障排除
当我尝试通过localhost上的转发端口连接到虚拟机时,我得到了“连接被拒绝”错误。
请尝试使用"127.0.0.1"而不是"localhost"作为主机名。
当我尝试通过SSH执行命令时,我得到了“连接被拒绝”错误。
请尝试使用相同设置在命令行上使用SSH连接到该机器。例如,如果您正在使用以下代码
shell = spur.SshShell(
hostname="remote",
port=2222,
username="bob",
private_key_file="/home/bob/.ssh/id_rsa"
)
with shell:
result = shell.run(["echo", "hello"])
请尝试运行
ssh bob@remote -p 2222 -i /home/bob/.ssh/id_rsa
如果ssh命令成功,请确保ssh.SshShell和ssh命令的参数相同。如果ssh.SshShell的任何参数是动态生成的,请尝试将其硬编码以确保它们设置为预期的值。
我无法通过SSH启动或运行命令
如果您在通过SSH启动或运行命令时遇到问题,请尝试将shell_type=spur.ssh.ShellTypes.minimal作为参数传递给spur.SshShell。例如
import spur
import spur.ssh
spur.SshShell(
hostname="localhost",
username="bob",
password="password1",
shell_type=spur.ssh.ShellTypes.minimal,
)
这会对主机shell支持的功能做出最小假设,特别适用于嵌入式系统中找到的最小shell。如果主机shell功能更全面,但只与spur.ssh.ShellTypes.minimal一起工作,请随时提交问题。
为什么shell功能(如变量和重定向)不起作用?
命令是直接运行的,而不是通过shell。如果您想使用任何shell功能(如变量和重定向),则需要在这些命令中运行适当的shell。例如
shell.run(["sh", "-c", "echo $PATH"])
shell.run(["sh", "-c", "ls | grep bananas"])
项目详情
下载文件
下载适用于您的平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。