概要
以下、自分用のメモです。忘れない内にメモメモ。
前回の記事は以下です。
上記のサンプル作っている際に知ったことなんですが、
if err = sess.Run(command); err != nil { output.Stderrl("[sess.Run]", err) return err }
として、コマンド実行した後に
defer func() { e := sess.Close() if e != nil { output.Stderrl("[sess.Close]", e) } }()
として、セッションを Close しようとすると、 io.EOF
が返ってきます。
何で?ってなって、調べてみると以下の情報を発見。
なるほど。。sess.Run() すると内部で実行後のセッションをCloseしているので、その後に更に Close 呼んでいることになります。
なので、io.EOF が返ってきていたということでした。
このエラーは返ってくるけど、無害なので無視してもオッケイですね。
以下のようにしました。
defer func() { // see: https://stackoverflow.com/questions/60879023/getting-eof-as-error-in-golang-ssh-session-close // // sess.Run() は、コマンドを実行後にセッションをcloseしている. // そのため、sess.Run() 後に sess.Close() を呼ぶと io.EOF が返却される. // 既にセッションは sess.Run() で適切にクローズされているため、このエラーは無視しても良い e := sess.Close() if e != nil && e != io.EOF { output.Stderrl("[sess.Close]", e) } }()
サンプル
以下のサンプルですが、処理に必要な情報を環境変数から取得するようにしています。
動かす場合は以下を設定しておいてください。
- $SSH_USER
- SSH接続するユーザ名
- $SSH_PASS
- 秘密鍵のパスワード
- $SSH_HOST
- 接続先を
xxx.xxx.xxx.xxx:ポート番号
の形式で。
- 接続先を
SSH接続したら、cat /etc/os-release | head -n 2
を実行して終わります。
package network import ( "fmt" "io" "os" "github.com/devlights/gomy/output" "golang.org/x/crypto/ssh" ) // SSHSessionCloseAfterRun -- ssh.Run() を呼んだ後に ssh.Close() を呼ぶと io.EOF が返却されることを確認するサンプルです. // // REFERENCES: // - https://stackoverflow.com/questions/60879023/getting-eof-as-error-in-golang-ssh-session-close func SSHSessionCloseAfterRun() error { // ----------------------------------------------------------------------------------------- // ssh.Run() した後に ssh.Close() すると io.EOF が返却される件 // // 理由としては、ssh.Run() を実行すると内部でセッションを Close しているため。 // 適切に セッション は閉じられているので、このエラーは無視しても良い。 // ----------------------------------------------------------------------------------------- var ( sshUser = os.ExpandEnv("${SSH_USER}") sshPass = os.ExpandEnv("${SSH_PASS}") sshHost = os.ExpandEnv("${SSH_HOST}") ) if sshUser == "" { return fmt.Errorf("[error] %s が設定されていません", "${SSH_USER}") } if sshPass == "" { return fmt.Errorf("[error] %s が設定されていません", "${SSH_PASS}") } if sshHost == "" { return fmt.Errorf("[error] %s が設定されていません", "${SSH_HOST}") } config := &ssh.ClientConfig{ User: sshUser, Auth: []ssh.AuthMethod{ ssh.Password(sshPass), }, HostKeyCallback: ssh.InsecureIgnoreHostKey(), } conn, err := ssh.Dial("tcp", sshHost, config) if err != nil { output.Stderrl("[ssh.Dial]", err) return err } sess, err := conn.NewSession() if err != nil { output.Stderrl("[conn.NewSession]", err) return err } defer func() { // see: https://stackoverflow.com/questions/60879023/getting-eof-as-error-in-golang-ssh-session-close // // sess.Run() は、コマンドを実行後にセッションをcloseしている. // そのため、sess.Run() 後に sess.Close() を呼ぶと io.EOF が返却される. // 既にセッションは sess.Run() で適切にクローズされているため、このエラーは無視しても良い e := sess.Close() if e != nil && e != io.EOF { output.Stderrl("[sess.Close]", e) } }() // リモートサーバでのコマンド実行結果をローカルの標準出力と標準エラーへ流す sess.Stdout = os.Stdout sess.Stderr = os.Stderr if err = sess.Run(command); err != nil { output.Stderrl("[sess.Run]", err) return err } return nil }
実行すると以下のようになります。
$ go run . ENTER EXAMPLE NAME: ssh_close_after_run [Name] "ssh_close_after_run" NAME="Ubuntu" VERSION="20.04 LTS (Focal Fossa)" [Elapsed] 305.0718ms
参考情報

プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)
- 作者:Alan A.A. Donovan,Brian W. Kernighan
- 発売日: 2016/06/20
- メディア: 単行本(ソフトカバー)

Go Programming for Network Operations: A Golang Network Automation Handbook (English Edition)
- 作者:McAllen, Tom
- 発売日: 2018/10/21
- メディア: Kindle版
過去の記事については、以下のページからご参照下さい。
- いろいろ備忘録日記まとめ
サンプルコードは、以下の場所で公開しています。
- いろいろ備忘録日記サンプルソース置き場