2013-11-28 30 views
6

我有挂起以下Python代码:蟒子通信冻结

cmd = ["ssh", "-tt", "-vvv"] + self.common_args 
cmd += [self.host] 
cmd += ["cat > %s" % (out_path)] 
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, 
         stdout=subprocess.PIPE, stderr=subprocess.PIPE) 
stdout, stderr = p.communicate(in_string) 

它应该为字符串(in_string)保存到通过ssh远程文件。

该文件被正确保存,但随后进程挂起。如果我使用

cmd += ["echo"] instead of 
cmd += ["cat > %s" % (out_path)] 

过程中不挂,所以我敢肯定,我误解了有关办法的东西传达认为,进程已退出。

你知道我应该如何编写命令,以便“cat>文件”不会使通信挂起?

+1

我认为这[post](http://stackoverflow.com/a/19202567/1982962)可以帮助,这是如何使用SSH写入远程文件的示例 –

+0

轻微切线,而不是使用SSH进程来做到这一点,你有没有考虑过像[SSHFS](http://fuse.sourceforge.net/sshfs.html)?这意味着你只需要担心写入文件,而不是保留所有的这个 –

回答

1

-tt选项分配的tty这可以防止子进程在4时退出关闭p.stdinEOF被忽略)。这工作:

import pipes 
from subprocess import Popen, PIPE 

cmd = ["ssh", self.host, "cat > " + pipes.quote(out_path)] # no '-tt' 
p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) 
stdout, stderr = p.communicate(in_string) 

你可以使用paramiko - 纯Python的ssh库,通过ssh将数据写入到远程文件:

#!/usr/bin/env python 
import os 
import posixpath 
import sys 
from contextlib import closing 

from paramiko import SSHConfig, SSHClient 

hostname, out_path, in_string = sys.argv[1:] # get from command-line 

# load parameters to setup ssh connection 
config = SSHConfig() 
with open(os.path.expanduser('~/.ssh/config')) as config_file: 
    config.parse(config_file) 
d = config.lookup(hostname) 

# connect 
with closing(SSHClient()) as ssh: 
    ssh.load_system_host_keys() 
    ssh.connect(d['hostname'], username=d.get('user')) 
    with closing(ssh.open_sftp()) as sftp: 
     makedirs_exists_ok(sftp, posixpath.dirname(out_path)) 
     with sftp.open(out_path, 'wb') as remote_file: 
      remote_file.write(in_string) 

其中makedirs_exists_ok()功能模仿os.makedirs()

from functools import partial 
from stat import S_ISDIR 

def isdir(ftp, path): 
    try: 
     return S_ISDIR(ftp.stat(path).st_mode) 
    except EnvironmentError: 
     return None 

def makedirs_exists_ok(ftp, path): 
    def exists_ok(mkdir, name): 
     """Don't raise an error if name is already a directory.""" 
     try: 
      mkdir(name) 
     except EnvironmentError: 
      if not isdir(ftp, name): 
       raise 

    # from os.makedirs() 
    head, tail = posixpath.split(path) 
    if not tail: 
     assert path.endswith(posixpath.sep) 
     head, tail = posixpath.split(head) 

    if head and tail and not isdir(ftp, head): 
     exists_ok(partial(makedirs_exists_ok, ftp), head) # recursive call 

    # do create directory 
    assert isdir(ftp, head) 
    exists_ok(ftp.mkdir, path) 
+0

但为什么它使用-tt + echo?你的解释似乎暗示它不应该与echo一起工作!? –

+0

@JeromeWAGNER:echo不希望输入stdin。尝试任何程序不会退出,直到所有输入(标准输入)被消耗。虽然我也不完全了解它。它可能是一个错误链中的关闭伪tty文件描述符。这是平台依赖,例如,这里是[使用'pty'的代码(这是我想像ssh启动远程子进程(我可以完全错误))](http://stackoverflow.com/a/12471855/4279 ) – jfs

0

这是有道理的,cat命令挂起。它正在等待EOF。我尝试在字符串中发送EOF,但无法使其正常工作。在研究这个问题之后,我发现了一个很好的模块,可以简化使用SSH进行命令行任务的过程,比如你的猫例子。它可能不是你的用例所需要的,但它确实可以解决你的问题。

pip install fabric 

安装fabric里面一个名为fabfile.py把

from fabric.api import run 

def write_file(in_string, path): 
    run('echo {} > {}'.format(in_string,path)) 

然后用下面的命令提示符下运行此,

fab -H [email protected] write_file:in_string=test,path=/path/to/file 
+0

感谢这个想法,但我正在尝试修补一个现有的项目(ansible),并且不会接受对结构的依赖。 –

+0

欢迎。但是,我认为它可能不适合您的需求。 Kobi K的答案是否适合你? –

+0

是的Kobi K的答案似乎工作,但我不明白与沟通的问题。我发现删除“-tt”会使其工作。这对我来说是一种神秘感。 –