4

有没有一种简单的方法可以将本地命令的输出通过管道传输到远程命令(反之亦然)?

我一直只是通过管道传输到一个文件,将文件移过来,然后读取它……但似乎有更简单的方法。

对于更简单的情况,只需捕获输出并使用字符串插值即可:

ip = local('hostname -i')
run('Script was run from ip: %s' % ip)

但是,当输出需要转义以在命令行上安全和/或需要来自标准输入时,这就有点棘手了。

如果输出是 bash 安全的,那么类似的东西run('echo "%s" | mycmd' % ip)会做我正在寻找的东西(我猜这意味着一个等效的问题是“是否有一种简单的方法来 bash-escape 字符串?”),但它似乎在那里应该是提供远程标准输入的“正确方法”。

编辑:

为了澄清 long-ish 输入,简单的字符串轮询存在许多潜在问题:经典的 shell 问题(例如,输出可能"; rm -rf /包含)。

认为只是做run("echo '%s' | cmd" % output.replace("'", "'\\''")应该有效,但可能会遗漏一些边缘情况。

正如我上面提到的,这似乎是织物可以通过直接向 run() 的标准输入发送一个字符串来为我更优雅地处理的类型(尽管我可能只是被它如此优雅地处理其他所有事情所宠坏了: )

4

3 回答 3

0

我已经这样做了一次,以便将(二进制)流发送到远程服务器。

这有点骇人听闻,因为它深入研究了 fabric 和 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的一部分

于 2014-07-23T10:44:46.327 回答
0

你可以发送带有 fexpect 的远程标准输入,我的结构扩展。这也会发送一个文件,但将其隐藏在 api 后面。不过,您仍然必须进行转义。

于 2012-04-22T18:18:49.977 回答
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字节的情况下因套接字超时而中止,并且它会打印远程命令的完整输出。

于 2017-08-10T15:57:10.477 回答