2013-11-28 33 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」會使其工作。這對我來說是一種神祕感。 –