Ввод ssh-подсказок
Таким образом, я могу подключиться к компьютеру по ssh, но у меня возникают проблемы при вводе данных в командной строке.
...
sshConfig := &ssh.ClientConfig{
User: user,
Auth: []ssh.AuthMethod{
ssh.Password(password),
},
HostKeyCallback: KeyPrint,
}
connection, err := ssh.Dial("tcp", connStr, sshConfig)
if err != nil {
log.Fatalln(err)
}
session, err := connection.NewSession()
if err != nil {
log.Fatalln(err)
}
modes := ssh.TerminalModes{
ssh.ECHO: 0, // disable echoing
ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
}
if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
session.Close()
log.Fatalf("request for pseudo terminal failed: %s", err)
}
stdin, err := session.StdinPipe()
if err != nil {
log.Fatalf("Unable to setup stdin for session: %v", err)
}
go io.Copy(stdin, os.Stdin)
stdout, err := session.StdoutPipe()
if err != nil {
log.Fatalf("Unable to setup stdout for session: %v", err)
}
go io.Copy(os.Stdout, stdout)
stderr, err := session.StderrPipe()
if err != nil {
log.Fatalf("Unable to setup stderr for session: %v", err)
}
go io.Copy(os.Stderr, stderr)
// err = session.Run("1")
session.Run("") // running it allows me to interact with the remote machines terminal in my own terminal.. session.Start("") exits and session.Wait() doesn't display the Welcome screen that normally greats users, and the prompt doesn't appear.
stdin.Write([]byte("10000"))
os.Stdin.WriteString("110000")
// log.Fatalln(n, err)
// os.Stdin.WriteString("1")
// for {
// session.Run("1")
// go os.Stdin.WriteString("1")
// go stdin.Write([]byte("10000"))
// }
...
Приведенный выше фрагмент кода помещает меня в машину, и на моем экране отображается приглашение машины, как будто я ssh'ed вручную. Я могу напечатать в оболочке... но мне нужно, чтобы в оболочке для меня был тип Go. Подсказка, с которой я взаимодействую, является текстовой игрой, поэтому я не могу просто давать команды, например, нет (ls, echo, grep и т. Д.), Единственное, что мне разрешено передавать, это числа. Как отправить входные данные в сессию ssh? Я пробовал много способов, и ни один из входных данных, кажется, не проходит.
Я также прилагаю снимок экрана с подсказкой, просто приведенное выше описание приводит к путанице в попытках изобразить тип сеанса.
ОБНОВЛЕНИЕ: Я думаю, что нашел способ отправить данные, по крайней мере, один раз.
session, err := connection.NewSession()
if err != nil {
log.Fatalln(err)
}
// ---------------------------------
modes := ssh.TerminalModes{
ssh.ECHO: 0, // disable echoing
ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
}
if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
session.Close()
log.Fatalf("request for pseudo terminal failed: %s", err)
}
stdin, err := session.StdinPipe()
if err != nil {
log.Fatalf("Unable to setup stdin for session: %v", err)
}
go io.Copy(stdin, os.Stdin)
stdout, err := session.StdoutPipe()
if err != nil {
log.Fatalf("Unable to setup stdout for session: %v", err)
}
go io.Copy(os.Stdout, stdout)
stderr, err := session.StderrPipe()
if err != nil {
log.Fatalf("Unable to setup stderr for session: %v", err)
}
go io.Copy(os.Stderr, stderr)
go session.Start("")
for {
stdin.Write([]byte("10000000\n"))
break
}
session.Wait()
Я начинаю сессию с go session.Start("")
помните, что нет смысла передавать команду, потому что все, что я делаю, это ввод данных в ответ на приглашение. Я тогда использую session.Wait()
в конце цикла for... вроде как при использовании каналов и групп ожидания.. внутри цикла for я отправляю данные stdin.Write([]byte("10000000\n"))
где важно отметить, чтобы добавить \n
разделитель для имитации нажатия клавиши ввода на клавиатуре.
Если есть какие-то лучшие способы добиться того, чего я пытаюсь достичь, пожалуйста, не стесняйтесь. Следующие шаги должны проанализировать стандартный ответ и ответить соответственно.
1 ответ
Пустой Start будет работать, однако в пакете ssh все Start, Run и Shell обращаются к одному и тому же. Start("cmd") выполняет команду внутри оболочки, Run("cmd") - это простой вызов Start ("cmd"), который затем вызывает для вас Wait() (что дает ощущение выполнения без параллелизма) и Shell открывает оболочку (например, Start), но без команды. На самом деле это шесть из одного, полдюжины других, но использование Shell(), вероятно, является самым чистым способом добиться этого.
Также имейте в виду, что Start() и Shell() используют параллелизм без явного вызова "go". Это может высвободить дополнительную миллисекунду или около того, чтобы вызвать параллелизм вручную, но если это не имеет значения для вас, тогда вы сможете отказаться от этого. Автоматический параллелизм Start и Shell является причиной для методов Wait() и Run ("cmd").
Если вам не нужно интерпретировать свои выходные данные (stdout/err), вы можете отобразить их без вызова pipe () или io.Copy(), что проще и эффективнее. Я сделал это в приведенном ниже примере, но имейте в виду, что если вы интерпретируете вывод, возможно, проще работать с Pipe(). Вы можете отправлять несколько команд (или чисел) последовательно, не читая подсказки в большинстве случаев, но некоторые вещи (например, запросы паролей) очищают входной буфер. Если это произойдет для вас, вам нужно будет прочитать стандартный вывод, чтобы найти подсказку, или воспользоваться инструментом ожидаемого, например, goexpect ( https://github.com/google/goexpect). Существует несколько ожидаемых пакетов для Go, но этот от Google и (на момент публикации) все еще поддерживается довольно недавно.
StdinPipe() предоставляет writeCloser, который можно использовать без io.Copy(), что должно быть более эффективным.
Ваш цикл for, который пишет в StdinPipe(), должен позволять вам вводить несколько команд (или, в вашем случае, наборы чисел)... в качестве примера, у меня есть эти команды чтения (числа и т. Д.) Из os.Args и итерации через них.
Наконец, вам, вероятно, следует добавить session.Close() для исправного завершения (у вас уже есть вызов для ошибок). Тем не менее, это то, что я бы порекомендовал (основываясь на вашем последнем примере):
modes := ssh.TerminalModes{
ssh.ECHO: 0, // disable echoing
ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
}
if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
session.Close()
log.Fatalf("request for pseudo terminal failed: %s", err)
}
defer session.Close()
stdin, err := session.StdinPipe()
if err != nil {
log.Fatalf("Unable to setup stdin for session: %v", err)
}
session.Stdout = os.Stdout
session.Stderr = os.Stderr
err = session.Shell()
if err != nil {
log.Fatalf("Unable to setup stdin for session: %v", err)
}
for _, cmd := range os.Args {
stdin.Write([]byte(cmd + "\n"))
}
session.Wait()
О, еще один момент, на который следует обратить внимание, это то, что метод Wait() опирается на непроверенный канал, который извлекает exitStatus из вашей команды, и это происходит в редких случаях (это особенно проблематично при подключении к механизму cisco, но может случиться и с другими). также). Если вы обнаружите, что вы столкнулись с этим (или вы просто хотите быть осторожным), вы можете обернуть Wait() в какую-то методологию тайм-аута, например, вызывая Wait() с параллелизмом и читая ответ через канал. это может быть рассмотрено вместе со временем.Afterfter () (могу привести пример, если это будет полезно).