2016-11-07 14 views
0

我有一个简单的scp函数,它只是scp cli工具的一个封装。两次写入相同的子进程golang

type credential struct { 
    username string 
    password string 
    host  string 
    port  string 
} 

func scpFile(filepath, destpath string, c *credential) error { 
    cmd := exec.Command("scp", filepath, c.username+"@"+c.host+":"+destpath) 

    if err := cmd.Run(); err != nil { 
     return err 
    } 

    fmt.Println("done") 
    return nil 
} 

这工作得很好现在我想添加在SSH中输入密码的功能,如果scp需要它。这是我想出的

func scpFile(filepath, destpath string, c *credential) error { 
    cmd := exec.Command("scp", filepath, c.username+"@"+c.host+":"+destpath) 
    stdin, err := cmd.StdinPipe() 
    if err != nil { 
     return err 
    } 
    defer stdin.Close() 

    if err := cmd.Start(); err != nil { 
     return err 
    } 

    io.WriteString(stdin, c.password+"\n") 
    cmd.Wait() 
    fmt.Println("done") 
    return nil 
} 

这不起作用,因为密码提示只是挂在那里。我尝试添加1秒睡眠,然后重新写入stdin,可能是因为我写密码的速度很快,但没有什么区别。

+1

这是因为SCP程序的性质,[不只是读密码正常](http://stackoverflow.com/questions/1340366/how-to-make-ssh-receive-the-password-from-stdin)。有各种各样的安全考虑因素,所以确保你看看细节。 – nothingmuch

+0

'scp'只是一个使用'ssh'作为传输工具。以与ssh相同的方式自动执行,最好使用publickey认证。 – JimB

回答

0

所以我找到了解决办法,而不是尝试发送密码到stdin我通过ssh会话创建了一个ssh会话和scp文件。这是新scpFile功能:

func scpFile(filePath, destinationPath string, session *ssh.Session) error { 
    defer session.Close() 

    f, err := os.Open(filePath) 
    if err != nil { 
     return err 
    } 
    defer f.Close() 

    s, err := f.Stat() 
    if err != nil { 
     return err 
    } 

    go func() { 
     w, _ := session.StdinPipe() 
     defer w.Close() 
     fmt.Fprintf(w, "C%#o %d %s\n", s.Mode().Perm(), s.Size(), path.Base(filePath)) 
     io.Copy(w, f) 
     fmt.Fprint(w, "\x00") 
    }() 
    cmd := fmt.Sprintf("scp -t %s", destinationPath) 
    if err := session.Run(cmd); err != nil { 
     return err 
    } 
    return nil 
} 

这也许可以变得更好,但主要的想法是有