SSH + здесь документ - Ctrl+C добраться до удаленной стороны?

В рамках серии задач, которые я должен выполнить, мне нужно создать bash-скрипт, который обращается к удаленному компьютеру, выполняет 3 команды, ожидает завершения процесса через SIGINT (или просто конец), а затем сделать некоторую очистку после этого.

В настоящее время я использую этот код:

#! /bin/bash

# local preparations
# ...

ssh -t $USER@remote.far <<-'COMMANDS'

    echo "Preparing execution"

    java -jar execute.jar &
    executePID=$!

    echo "Ready."
    echo "CTRL+C to clean and close"

    trap "kill $executePID" INT HUP
    wait $executePID

    #cleanup code here

    echo "done. Logging out"
    sleep 2
    logout
COMMANDS

# Final local cleanup

Этот код, кажется, работает только найти, пока wait (встроенная) команда. wait кажется, потребляет все команды, которые идут после него и так, когда я пытаюсь отправить SIGINT (Ctrl+C), кажется, не удается выполнить содержимое прерывания и весь код очистки.

Как я могу это исправить, чтобы все работало так, как я ожидаю?

Мне не разрешено разбивать этот bash-файл на несколько, и мне не разрешается создавать какие-либо сценарии на удаленном компьютере, даже если они временные.
Оба компьютера работают под управлением Linux.

1 ответ

объяснение

Это не о wait, Я думаю, что это то, что происходит:

Ты используешь <<, так stdin из ssh перенаправляется на некоторый дескриптор, через который проходит весь документ.

Этот другой ответ объясняет, как ssh может захватить Ctrl+C, когда -t используется. Вот что важно:

На стороне клиента ssh постараюсь установить tty использован stdin в "сырой" режим […] Установка необработанного режима означает, что символы, которые обычно посылают сигналы (например, Ctrl+C), вместо этого просто вставляются во входной поток.

В вашем случае это не то же самое stdin ваша локальная оболочка использует. tty используемая вашей локальной оболочкой остается нетронутой, она никогда не устанавливается в "сырой" режим.

Поэтому, когда вы нажимаете Ctrl+C, он действует локально и завершается ssh, В этот момент удаленная сторона получает SIGHUP, Ваш trap работает и убивает java, Я думаю, что здесь есть подводный камень: trap выполняет некоторый код в ответ на данный сигнал, но это не препятствует тому, чтобы сигнал имел нормальный эффект. Поэтому мне кажется, что ваш java был бы убит даже без trap потому что это работа оболочки, которая завершается в ответ на SIGHUP,

Завершенная оболочка перестает читать и интерпретировать свою stdin, Вот почему все, что следует wait отбрасывается


Решение

commands() { cat <<-'COMMANDS'

    cleanup() {
    # cleanup code here
    echo "Done. Logging out"
    sleep 2
    logout
    }

    echo "Preparing execution"

    java -jar execute.jar &
    executePID=$!

    echo "Ready."
    echo "CTRL+C to clean and close"

    trap "kill $executePID; cleanup" INT HUP
    wait $executePID

    cleanup
COMMANDS

}

stty raw -echo; cat <(commands) - | ssh -t $USER@remote.far; stty -raw echo

Последняя строка - фактическая команда. Сначала мы готовим tty поэтому Ctrl+C не может действовать локально. Затем мы объединяем команды и стандартный ввод, передаем его в удаленную оболочку. Я не могу сделать это с этим документом напрямую, command функция это обходной путь (было бы проще использовать обычный файл, но вы сказали, что не можете использовать более одного). После ssh а также cat выход мы устанавливаем tty в нормальном состоянии.

Наличие псевдотерминала очень важно, поэтому убедитесь, что ssh -t работает (использовать -tt если нужно).

Удаленная оболочка должна читать (буферизовать) все команды из commands, только тогда он может получить Ctrl+C при нажатии. Я думаю, это означает, что вы не можете иметь слишком много кода после wait, Кроме того, что вы хотите сделать после SIGINT должен быть запущен изнутри trap, Вот почему я использовал один cleanup функция, которая делает все это.

Код не является надежным. Нажмите Ctrl+C слишком рано или несколько раз, и вы окажетесь в удаленной оболочке или с несколько "сломанной" локальной tty, В этом последнем случае используйте команду reset сбросить ваш tty,

Вы должны нажать клавишу (например, Enter) после того, как увидите "Соединение с… закрыто". Причина в cat не заметит, что труба сломана (из-за ssh не более), пока он не попытается что-то написать.


альтернатива

Если по какой-либо причине вышеуказанное решение не работает для вас, воспользуйтесь этой более простой альтернативой. Здесь документ в точности как раньше. В этом случае мы не связываемся с ttyCtrl+C завершает локальный ssh как это происходит с вашим исходным кодом. Разница (по отношению к вашему коду) заключается в trap делает уборку. Я думаю, этого будет достаточно, чтобы поймать SIGHUP только.

ssh -t $USER@remote.far <<-'COMMANDS'

    cleanup() {
    # cleanup code here
    echo "Done. Logging out"
    sleep 2
    logout
    }

    echo "Preparing execution"

    java -jar execute.jar &
    executePID=$!

    echo "Ready."
    echo "CTRL+C to clean and close"

    trap "kill $executePID; cleanup" INT HUP
    wait $executePID

    cleanup
COMMANDS

Примечание: когда trap срабатывает cleanup повторяет сообщение, но вы его не увидите, потому что ваш местный ssh уже отключен. Вы увидите сообщение, только если java выход без trap, Еще твой код очистки (# cleanup code here) должен быть выполнен.

Другие вопросы по тегам