
Я делаю первые шаги по написанию запланированного скрипта оболочки. Цель — написать простой скрипт резервного копирования для сервера Minecraft. Сервер работает в своем собственном имениэкран.
Я предполагаю, что если мой сценарий содержит что-то вроде
screen -S $(screen_name) -p 0 -X stuff "save-all^M"
серверная команда займет некоторое время. Мой вопрос: скрипт продолжит работу немедленно или будет ждать, пока команда на доступном экране не будет завершена? Если он вернется немедленно, как я могу дождаться его (в этом случае команда save-all
сохраняет текущий мир. Следующим шагом будет копирование файлов в резервную папку, поэтому я, очевидно, хочу дождаться, пока мир будет правильно сохранен)?
решение1
Нет. screen -X stuff
Команда вернется немедленно, потому что онане знаютчто его просят выполнить команду в первую очередь, тем более, когда эта команда завершена – все, что он делает, это вводит нажатия клавиш (вот почему вам пришлось добавить ^M
вручную). Как только поддельный ввод tty был отправлен, команда возвращается, и ваш скрипт продолжается.
В целом, как эмулятор терминала, screen не знает, что именно происходит в каждом конкретном окне терминала — нет ничего, что могло бы отделить приглашение оболочки (или любое другое интерактивное консольное приглашение) от обычного вывода.
(Это не является принципиально невозможным — некоторые эмуляторы терминала позволяют оболочке выводить маркеры «начало/конец вывода» и «начало/конец приглашения», напримерVSCodeвнедрит специальную конфигурацию в Bash, чтобы добиться именно этого, но для этого требуется поддержка эмулятора терминалаи(Возможность сотрудничества с каждой программой, которая будет отображать запрос на ввод данных, а ни Screen, ни Minecraft в настоящее время этого делать не будут.)
С другой стороны, вашему скрипту может быть проще, поскольку он имеет дело только с одной программой, а это значит, что ему нужно только ждатьспецифическийprompt появится, а не просто любой prompt вообще. Вы могли бы реализовать это, имея цикл, который запросит Screen для текущего буфера "содержимое" с помощью -X hardcopy
, и если последняя строка этого буферанетпока не появилось приглашение Minecraft, подождите 1 секунду и повторите.
Это похоже на то, как expect
работает программа: можно написать сценарий Expect для автоматизации различных видов интерактивного ввода, но ядром такого сценария всегда является набор expect "this"
и expect "that"
, т. е. заранее известно, что некоторый конкретный текст является «подсказкой», и ожидается этот текст.
[...]
expect "Password:" { send "$password\r" }
expect ">" { send "enable\r" }
expect "Password:" { send "$enablepwd\r" }
expect "#" { send "show run\r" }
Что делают другие скрипты управления Minecraft (например,этот проект, который недавно перешел с Screen на tmux), похоже, 1) слепо отправляет и "save-all", и "stop" одновременно, 2) ждет, пока сервер обработает команды бота, пока он сам не выйдет. То есть, вместо того, чтобы ждать, покакомандадля завершения они ждутрезультатэтого приказа.
Аналогично, если вы хотите подождатьсохранятьдля завершения вам не нужно ждать команду "save-all" – вместо этого вы можете использовать , inotifywait
чтобы дождаться, пока сервер закончит запись определенного файла. Это может быть довольно сложно сделать правильно (обычно вам нужно использовать "coproc" для запуска inotifywaitдоотдав команду, чтобы не пропустить событие), но поскольку сохранение, скорее всего, займет некоторое время, этовероятнонормально просто сделать это позже.
echo "waiting..."
inotifywait -q -e close_write /path/to/game
echo "probably done!"
Некоторые программы намеренно создают определенный файл последним, чтобы другие инструменты могли дождаться его появления:
echo "waiting..."
until [ -e /path/to/marker_file ]; do sleep 1; done
echo "marker file showed up"