ожидаем выхода {} без тайм-аута - PullRequest
8 голосов
/ 29 июня 2010

Я борюсь с классической проблемой ввода пароля автоматически в ssh, и, как и все остальные, я спотыкаюсь в неведении относительно ожидания.Наконец, я собрал воедино скрипт, который вроде работает:

#!/usr/bin/expect -f

# command line args
set user_at_host [lrange $argv 0 0]
set password [lrange $argv 1 1]

set timeout 1

# ssh command
spawn ssh -S ~/.ssh/tmp.$user_at_host -M -N -f $user_at_host

# deal with ssh prompts
expect {
    "*yes/no*" { send "yes\r" ; exp_continue }
    "*assword:" { send "$password\r" ; exp_continue }
}

Этот скрипт завершается только благодаря строке timeout 1, без него он просто зависает и завершается только при взаимодействии с пользователем (^C).

Когда строка spawn была прямой командой ssh, сценарий сразу заканчивался, однако это не ваш прямой ssh ​​.Отличительной особенностью может быть опция -f, которая запускает его в фоновом режиме (но я пробовал сценарий без него).

Я прочитал, что interact или expect eof могут помочь, но я не смог найти правильное заклинание, которое действительно сделает это.

Мой вопрос (я думаю): Как создать ожидаемый сценарий, который порождает фоновый процесс, завершить безtimeout?


Редактировать: я должен был ожидать (не каламбур) ответ "использовать аутентификацию ssh без пароля".Хотя это разумный совет, в моем сценарии это не подходящее решение: автоматическое тестирование установленной системы vanilla в доверенной среде, где добавление доверенных ключей к образу нежелательно / невозможно.

Ответы [ 4 ]

7 голосов
/ 30 июня 2010

Вы, вероятно, хотите:

expect {
    "*yes/no*" { send "yes\r" ; exp_continue }
    "*assword:" { send "${password}\r" }
}
expect $the_prompt
send "exit\r"
expect eof

ОБНОВЛЕНИЕ

Я пропустил, что вы отправляете команду через ssh.Я думаю, все, что вам нужно, это:

spawn ssh a@b foo bar baz
expect {
    "*yes/no*" { send "yes\r" ; exp_continue }
    "*assword:" { send "${password}\r" }
    eof
}

Вы нажмете eof, когда команда foo завершится.

2 голосов
/ 30 июня 2010

ОК, поэтому я нашел перестановку, которая, кажется, работает -

Сначала мне нужен wrapper скрипт, который даст указание, когда закончите:

#!/bin/bash
"$@"
echo "done"

Тогда ожидаемый сценарий становится:

#!/usr/bin/expect -f
set user_at_host [lrange $argv 0 0]
set password [lrange $argv 1 1]

# no need for timeout 1
set timeout 60

# use the wrapper
spawn wrapper ssh -S ~/.ssh/tmp.$user_at_host -M -N -f $user_at_host

expect {
   "*yes/no*" { send "yes\r" ; exp_continue }
   "*assword:" { send "$password\r" ; exp_continue }
   # use the wrapper
   "done" { exit }
}

Спасибо за Дугласу Лидеру (проголосовал) и Гленн Джекману (проголосовал) за полезный совет. Я с радостью приму этот ответ и приму любой более элегантный ответ, возможно, такой, который покончит со скриптом-оберткой.

Спасибо всем за внимание.

1 голос
/ 29 июня 2010

Этот цикл:

expect {
    "*yes/no*" { send "yes\r" ; exp_continue }
    "*assword:" { send "${password}\r" ; exp_continue }
}

Не может завершиться никаким способом, кроме тайм-аута или EOF; две совпадающие строки будут exp_continue, поэтому снова закруглите цикл.

Уход на задний план означает в основном разветвление; родитель умирает, а ребенок продолжает соединение.

Лично я бы по-другому решил интерактивные элементы:

  1. Ключи хоста: либо подключитесь один раз вручную, либо вставьте ключ непосредственно в файл ssh known_hosts.
  2. Пароль: я бы использовал аутентификацию с закрытым / открытым ключом. Либо используйте агент для хранения ключа, либо используйте ключ без пароля.
0 голосов
/ 31 июля 2014

как я это сделал:

set user "****"    
set host "127.0.0.1"    
spawn shh $user@$host    
expect "password: "    
send "**??54"    
expect "$"    
interact

(ответ на ваш вопрос об использовании метода взаимодействия)

...