是否有一种简单的方法可以将输出从本地命令传输到远程命令(反之亦然)?
我一直只是通过管道传输文件,移动文件,然后阅读它...但似乎可以有一种更简单的方法。
对于更简单的情况,只需捕获输出并使用字符串插值即可:
ip = local('hostname -i')
run('Script was run from ip: %s' % ip)
但是当输出需要转义为在命令行上安全和/或需要来自stdin时,它有点棘手。
如果输出是bash-safe,那么像run('echo "%s" | mycmd' % ip)
这样的东西会做我正在寻找的东西(我猜这意味着一个相同的问题就是“是否有一种简单的方法来bash-escape字符串? “),但似乎应该有一个”正确的方法“来提供远程标准输入。
编辑:
用long-ish输入来澄清简单字符串插入的许多潜在问题:经典的shell问题(例如,输出可能包含"; rm -rf /
)但是(在我的情况下更实际地)输出可以包含引号(单引号和双引号)。
我认为只是做run("echo '%s' | cmd" % output.replace("'", "'\\''")
应该有效,但可能会有边缘情况错过。
正如我上面提到的,这似乎是织物可以通过直接将一个字符串发送到run()的stdin来为我更优雅地处理的事物类型(尽管我可能只是因为处理其他所有事情而被宠坏了)如此优雅:)
答案 0 :(得分:0)
您可以发送remote stdin with fexpect, my fabric extension。这也发送一个文件,但隐藏在api后面。你仍然需要逃避。
答案 1 :(得分:0)
为了将(二进制)流发送到远程服务器,我已经完成了一次。
它有点hackish,因为它深入挖掘面料和paramiko的渠道,可能有未经测试的边缘情况,但它似乎主要是做这项工作
def remote_pipe(local_command, remote_command, buf_size=1024*1024):
'''executes a local command and a remote command (with fabric), and
sends the local's stdout to the remote's stdin'''
local_p= subprocess.Popen(local_command, shell=True, stdout=subprocess.PIPE)
channel= default_channel() #fabric function
channel.set_combine_stderr(True)
channel.settimeout(2)
channel.exec_command( remote_command )
try:
read_bytes= local_p.stdout.read(buf_size)
while read_bytes:
channel.sendall(read_bytes)
read_bytes= local_p.stdout.read(buf_size)
except socket.error:
local_p.kill()
#fail to send data, let's see the return codes and received data...
local_ret= local_p.wait()
received= channel.recv(buf_size)
channel.shutdown_write()
channel.shutdown_read()
remote_ret= channel.recv_exit_status()
if local_ret!=0 or remote_ret!=0:
raise Exception("remote_pipe failed. Local retcode: {0} Remote retcode: {1} output: {2}".format(local_ret, remote_ret, received))
如果有人想要提供修改,这是btrfs-send-snapshot
的一部分答案 2 :(得分:0)
这是@ goncalopp答案的略微改进版本:
def remote_pipe(local_command, remote_command, buffer_size=1024*1024, channel_timeout=60):
'''executes a local command and a remote command (with fabric), and
sends the local's stdout to the remote's stdin'''
local_process = Popen(local_command, shell=True, stdout=PIPE)
channel = default_channel() # Fabric function
channel.set_combine_stderr(True)
channel.settimeout(channel_timeout)
channel.exec_command(remote_command)
try:
bytes_to_send = local_process.stdout.read(buffer_size)
while bytes_to_send:
channel.sendall(bytes_to_send)
bytes_to_send = local_process.stdout.read(buffer_size)
except socket.error:
# Failed to send data, let's see the return codes and received data...
local_process.kill()
local_returncode = local_process.wait()
channel.shutdown_write()
remote_output = ""
try:
bytes_received = channel.recv(buffer_size)
while bytes_received:
remote_output += bytes_received
bytes_received = channel.recv(buffer_size)
except socket.error:
pass
channel.shutdown_read()
remote_returncode = channel.recv_exit_status()
print(remote_output)
if local_returncode != 0 or remote_returncode != 0:
raise Exception("remote_pipe() failed, local return code: {0}, remote return code: {1}".format(local_returncode, remote_returncode, remote_output))
除了可读性之外,改进之处在于,如果远程命令输出少于buffer_size
个字节,它不会因套接字超时而中止,并且它会输出远程命令的完整输出。