dpvhv66448 2018-07-24 15:19
浏览 48
已采纳

从* os.file复制Golang而无需等待EOF

I'm trying to copy from a file in go using io.Copy, which waits for an EOF before actually copying the bytes out of its internal buffer, correct? In my use case (PTY/SSH session) the EOF only comes when the session completes, which means I am flying blind the whole time until the session decides that its over.

I've tried to use CopyN with 1 byte at a time which does work, but if I try to wait for a certain bit of text to come up and I copy one past what has been pushed to the file the code will hang and I lose the session. Is there a function to just "read whats there" and then stop, or a different marker (like EOF) that can tell copy to stop for now?

I have also tried to read the contents of the file that ptyI.pty points to but it always returns 0 bytes so I cant check for updates there

Here is the code that handles it as of right now:

type PtyInterface struct {
    pty          *os.File
    buf          *bytes.Buffer
}

func (ptyI *PtyInterface) PrivCmd(cmdStr string) (string, error) {

    // Copy the command provided into the STDIN of the bash shell we opened with
    // the earlier PtyInterface
    _, _ = io.Copy(ptyI.pty, strings.NewReader(string("somecommand")))

    // Assuming everything has gone well, we wait for the prompt to appear

    // We do this by running through individual bytes until the prompt is
    // fully printed (otherwise we might try to send in the answer at the wrong time)
    for !strings.HasSuffix(ptyI.buf.String(), "Prompt question? ") {
        _, _ = io.CopyN(ptyI.buf, ptyI.pty, 1)
    }

    // Once we hit the prompt we throw the answer into STDIN along with a newline
    // and the bash shell should accept this and begin executing the command.
    _, _ = io.Copy(ptyI.pty, strings.NewReader(string("answer
")))

    // If we dont throw an exit in there then the PTY will never receive an EOF marker and we'll
    // hang on the next copy
    _, _ = io.Copy(ptyI.pty, strings.NewReader(string("exit
")))

    // Now this copy will wait for an EOF
    _, _ = io.Copy(ptyI.buf, ptyI.pty)

    //Debug info to be printed after
    fmt.Println("
Bytes written to buffer (newone): 
" + ptyI.buf.String())

    return ptyI.buf.String(), nil
}
  • 写回答

1条回答 默认 最新

  • duanan1946 2018-07-25 01:47
    关注

    Think of io.Copy as a convenance function for bulk copying or streams, not the right tool for request/response patterns.

    Just accumulate bytes into messages by checking if it matches the message on every byte. Use the Read method directly.

    func Expect(message string, r io.Reader) (resp string, err error) {
        b := []byte{0} // 1 byte buffer
        var n int
    
        for err == nil {
            n, err = r.Read(b)
            if n == 0 {
                continue
            }
            resp += string(b[0])
            if strings.HasSuffix(resp, message) {
                return resp, err
            }
        }
    
        return resp, err
    }
    

    In your example you could use this like:

    resp, err := Expect("Prompt question? ", ptyI.pty)
    

    Here's a demonstration of it in action with a simulated connection io.Reader: playground.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 素材场景中光线烘焙后灯光失效
  • ¥15 请教一下各位,为什么我这个没有实现模拟点击
  • ¥15 执行 virtuoso 命令后,界面没有,cadence 启动不起来
  • ¥50 comfyui下连接animatediff节点生成视频质量非常差的原因
  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题
  • ¥15 slam rangenet++配置
  • ¥15 有没有研究水声通信方面的帮我改俩matlab代码
  • ¥15 ubuntu子系统密码忘记
  • ¥15 保护模式-系统加载-段寄存器