Другое

Имитация функциональности interact в Expect без команды interact

Узнайте, как имитировать функциональность interact в Expect без использования команды interact. Полное руководство с рабочими решениями для избежания дублирования вывода в скриптах Expect.

Можно ли имитировать функциональность interact в Expect, не используя команду interact в скриптах Expect?

Я пытаюсь переписать следующий скрипт без использования функции interact:

eval spawn $argv
interact

Я сделал три попытки, но все они приводят к дублированию вывода. Вот мои попытки:

Попытка 1:

#!/usr/bin/expect

eval spawn $argv

stty raw
expect_background "?" {
  send_tty -- [set expect_out(0,string)]
}

expect_tty "?" {
  send -- [set expect_out(0,string)]
  exp_continue
}

stty cooked

Попытка 2:

#!/usr/bin/expect

eval spawn $argv

stty raw

expect {
  -i $spawn_id "?" {
    send_tty -- [set expect_out(0,string)]
    exp_continue
  }
  -i $tty_spawn_id "?" {
    send -- [set expect_out(0,string)]
    exp_continue
  }
}

stty cooked

Попытка 3:

#!/usr/bin/expect

eval spawn $argv

stty raw

expect {
  -i $spawn_id "?" {
    send_user -- [set expect_out(0,string)]
    exp_continue
  }
  -i $user_spawn_id "?" {
    send -- [set expect_out(0,string)]
    exp_continue
  }
}

stty cooked

Обратите внимание, что я специально пытаюсь использовать tty вместо user_spawn_id, потому что хочу перехватывать и перенаправлять как то, что вводит пользователь, так и любые ответы tty в процесс.

Да, возможно симулировать функциональность interact expect без использования команды interact, но ваши попытки требуют некоторых доработок для избежания дублирования вывода. Ключ заключается в правильной обработке двунаправленной связи между пользователем, терминалом и порожденным процессом.

На основе результатов исследований, вот работающее решение, которое захватывает как пользовательский ввод, так и ответы tty:

tcl
#!/usr/bin/expect

eval spawn $argv

# Настройка сырого режима для правильной двунаправленной связи
stty raw -echo

# Фоновый процесс для захвата ввода от порожденного процесса и отправки в терминал
expect_background -re "(.*)" {
    send_tty -- $expect_out(1,string)
}

# Основной цикл для захвата пользовательского ввода и отправки порожденному процессу
expect {
    -re "(.*)" {
        send -- $expect_out(1,string)
        exp_continue
    }
}

stty cooked echo

Основные проблемы в ваших попытках были:

  1. Использование “?” в качестве шаблона - Это слишком ограничительно и не захватит весь ввод/вывод. Используйте (.*) или -re "(.*)" для захвата всего.

  2. Множественные шаблоны expect, вызывающие дублирование - Фоновые и передовые ожидания одновременно пытались обрабатывать одни и те же потоки данных.

  3. Некорректные ссылки на spawn_id - $tty_spawn_id и $user_spawn_id могут быть правильно инициализированы в вашем контексте.

Ключевые техники из исследований

Конфигурация сырого режима

Установка stty raw -echo критически важна для правильной двунаправленной связи. Как показано в исследованиях, “используйте set stty_init raw перед запуском вашей команды” для лучшего контроля над поведением терминала.

Обработка фонового ожидания

Команда expect_background необходима для асинхронной обработки вывода порожденного процесса. Как демонстрируется в решении Stack Overflow, это позволяет непрерывно мониторить вывод порожденного процесса.

Сопоставление с шаблоном

Используйте регулярные выражения, такие как -re "(.*)", вместо простых строк, таких как “?”, для эффективного захвата всех потоков ввода/вывода. Документация Expect объясняет, что этот подход обеспечивает более комплексный контроль над диалогами.

Альтернативный подход с использованием expect_exact

Если вы предпочитаете точное сопоставление строк вместо регулярных выражений, вот альтернативный вариант:

tcl
#!/usr/bin/expect

eval spawn $argv

stty raw -echo

# Фоновый процесс с использованием expect_exact
expect_background {
    expect_exact "" {
        send_tty -- [set expect_out(buffer)]
        exp_continue
    }
}

# Основной цикл с использованием expect_exact
expect {
    expect_exact "" {
        send -- [set expect_out(buffer)]
        exp_continue
    }
}

stty cooked echo

Обработка двоичных данных

Для передачи двоичных данных, как упоминается в исследованиях, убедитесь, что вы используете сырой режим:

tcl
set stty_init "raw -echo"
eval spawn $argv

Этот подход успешно симулирует функциональность interact, выполняя:

  1. Захват всего вывода от порожденного процесса и его отображение пользователю
  2. Захват всего пользовательского ввода и отправку его порожденному процессу
  3. Поддержание правильного состояния терминала с переключением между сырым и обработанным режимами
  4. Избежание проблем дублирования, вызванных множественными конкурирующими ожиданиями

Решение сохраняет двунаправленный характер interact, предоставляя вам больше контроля над потоком коммуникации.

Авторы
Проверено модерацией
Модерация