- Вопрос или проблема
- 2 слова – ЭКРАННЫЕ ПОСЛЕДОВАТЕЛЬНОСТИ
- вкратце
- Подробности
- ?47h и ?47l
- Как нам восстановить положение курсора?
- И победителем является…
- ?1049h и ?1049l
- Финальное решение
- Ответ или решение
- Решение с использованием terminfo и команды tput
- Использование управляющих последовательностей ANSI
- Заключение
Вопрос или проблема
Из bash-скрипта, который занимает весь терминал, как восстановить предыдущую сессию?
Несколько программ, которые, как я знаю, работают таким образом:
- ranger
- vim
- man
- nano
Общая схема такова: программа занимает весь терминал, а затем, при выходе, все восстанавливается, т.е. введенные команды и выведенный текст.
Вы должны использовать опции terminfo cup
. Это можно сделать из оболочки с помощью команды tput
.
например
tput smcup
clear
echo hello
echo there
read
tput rmcup
Это очистит экран, выведет две строки, подождет, пока вы нажмете RETURN, и затем восстановит экран в исходное состояние.
Это требует, чтобы в определении terminfo
вашего терминала поддерживалась эта возможность. Не все терминалы обладают этой способностью.
Использование clear
или tput clear
сбивает историю оригинального экрана при использовании в bash-скрипте. Экран все еще там, но только видимая часть. Вся история исчезает.
Мне понадобилось много времени, чтобы узнать, что clear
должен быть заменен на tput cup 0 0
(работает в моем случае, перемещая курсор в верхний левый угол).
tput smcup
tput cup 0 0 # позиционировать курсор в верхний левый угол, имитировать очистку
...скрипт
tput rmcup
Да, я знаю, что я опоздал на этот праздник. Я наткнулся на эту тему, пытаясь разобраться в этом сам, будучи новичком, очень давно. После того, как наконец-то разобрался, меня несколько раздражает, что эта информация не была проще найти, не зная сверхсекретного языка для ее описания. Такова природа борьбы “самоучек”. Это все еще актуально, и, надеюсь, это спасет кого-то от разочарования.
ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ – Это дубликат моего ответа на очень похожий вопрос на stackoverflow.
2 слова – ЭКРАННЫЕ ПОСЛЕДОВАТЕЛЬНОСТИ
Я использую GNU bash, версия 5.2.15(1) на Debian Bookworm 12.8 lxc под Proxmox 8.3.2
вкратце
# переключиться на альтернативный(временный) буфер
echo -e '\e[?1049h'
# переключиться на основной буфер в оригинальном положении курсора
echo -e '\e[?1049l'
С этой информацией на руках вы можете изменить ваш скрипт следующим образом
#!/usr/bin/env bash
echo -e '\e[?1049h'
### выполнение скрипта
echo -e '\e[?1049l'
Подробности
Я долго искал, как это сделать, много лун назад. Метод tput
никогда мне не помогал. Я добивался успехов, используя tput
для других целей, но не в этом.
После множества других поисков я сдался.
Я стал креативным. Я даже пытался использовать tmux
, хотя это означало установку дополнительных пакетов. Ничто не работало так, как мне хотелось.
Я нашел пост о том, что nano и другие используют terminfo и termcap для достижения этого. Этот метод был выше моих способностей в то время, и, вероятно, все еще выше. Я не смог заставить это работать также…возможно из-за синдрома новичка.
Прошло время. Я переключился на другое.
Прошло довольно много времени с тех пор.
Когда я сегодня изучал последовательности escape, я наткнулся на эту удивительную страницу github:
Эти методы основаны на том, что я там нашел. Бесплатно – мой любимый цвет, и просто – мой любимый метод. Это и то, и другое.
Можно использовать как echo
, так и printf
. Я склонен использовать echo
?47h
и ?47l
Чтобы сохранить текущий экран –
echo -e '\e[?47h'
Вы можете выполнить скрипт и затем восстановить ранее сохраненный экран с помощью –
echo -e '\e[?47l'
Пример –
#!/usr/bin/env bash
echo -e '\e[?47h'
# выполнение задач
echo -e '\e[?47l'
Несколько наблюдений :
-
“Альтернативный буфер”, он же второй экран, всегда начинается очищенным, кроме приглашения.
-
reset
очистит сохраненный буфер экрана и данные о положении курсора, поэтому используйтеclear
. Однако переменные сохраняются. Вы можете использовать метод ниже для сохранения положения курсора приreset
. -
Экранирование любой из этих последовательностей выводит также и новые строки. Количество новых строк такое же, как если бы вы не переключали экраны.
-
Положение курсора всегда сохраняется при переключении на альтернативный экран, но только иногда в обратном направлении. Это зависит от используемой последовательности для возврата на основной экран.
-
Когда положение курсора сохраняется между экранами, оно остается там, где оно было при переключении экрана. Это означает, что когда вы восстановите экран, вы можете оказаться в середине содержимого экрана.
Как нам восстановить положение курсора?
Один из способов справиться с этим – использовать эти другие последовательности для сохранения и восстановления положения курсора:
# сохранить положение курсора
echo -e '\e[s'
# -или-
echo -e '\e[?1048h'
# восстановить положение курсора
echo -e '\e[u'
# -или-
echo -e '\e[?1048l'
Вы также можете записать положение курсора в переменных следующим образом: (этот read
ведет себя странно при использовании в приглашении, но все равно работает)
# read
# -r (разрешить esc)
# -s (скрытый)
# -dx (разделять символом 'x', в данном случае 'R')
# -p (подсказка -- посылает escape-последовательность)
IFS=";[" read -r -s -dR -p $'\E[6n' ненужная строка строка столбец # ненужная строка - это переменная для отбрасывания содержащая ESC
echo "Строка : ${строка}"
echo "Столбец : ${столбец}"
…и установите его снова, используя
echo -e '\e[${строка};${столбец}H'
Ссылаемый github сообщает, что вы можете установить строку и столбец с помощью \e[<строка>;<столбец>H
. Это может показаться, как будто столбец не устанавливается, если вы просто echo
выходную последовательность без текста. Установка столбца сохраняется для текста, который следует в echo
и затем переопределяется приглашением.
И победителем является…
Есть еще один вариант, если вы не хотите возиться с позиционированием курсора с помощью echo или printf.
?1049h
и ?1049l
Согласно xterm control sequences, 1047(то же что и 47) будет просто переключать между режимами экрана без сохранения или восстановления положения курсора. 1048 сохранит или восстановит положение курсора.
Мои наблюдения по 1048
таковы
echo -e '\e[?1048h' # сохранит положение курсора для текущего экрана
echo -e '\e[?1048l' # восстановит положение курсора для текущего экрана
Таким образом можно сохранять и восстанавливать отдельные положения курсора для каждого экрана.
1049 является комбинацией 1047 и 1048.
Финальное решение
\e[?1049h
сохраняет положение курсора основного буфера и переключает на альтернативный буфер. \e[?1049l
вернет курсор в сохраненное положение и восстановит основной буфер.
echo -e '\e[?1049h' # сохранить положение курсора и переключиться на альтернативный буфер(экран)
echo -e '\e[?1049l' # вернуться из альтернативного буфера на сохраненное положение курсора
Мой предыдущий пример был бы лучше написан так
#!/usr/bin/env bash
echo -e '\e[?1049h'
# выполнение задач
echo -e '\e[?1049l'
Я также обнаружил, что \e[?1049l
можно использовать для многократного возврата курсора в ту же сохраненную точку на основном буфере после того, как она была сохранена с помощью \e[?1049h
…даже после очистки экрана и многократного переключения вперед и назад, если вы используете \e[?1047h
(47), чтобы включить альтернативный экран. Использование \e[?1049h
снова перезапишет сохраненное положение курсора текущим. Использование \e[?1049h
на альтернативном экране сохранит положение курсора для альтернативного экрана, так же как и \e[?1048h
.
Ответ или решение
Для решения проблемы восстановления сеанса после выполнения shell-скрипта в Bash, когда программа занимает весь экран терминала и требуется восстановить предыдущую сессию с командами и выводом, есть несколько подходов. Важно отметить, что цель состоит в сохранении состояния экрана до запуска программы и его восстановлении после завершения.
Решение с использованием terminfo и команды tput
-
Подготовка окружения:
- Используйте
tput smcup
для переключения на альтернативный экран, который сохраняет текущее состояние терминала. - Команда
tput
предоставляет пользовательский интерфейс к capabilities библиотеки terminfo, что позволяет более гибко управлять терминалом.
- Используйте
-
Сброс и очистка экрана:
- Вместо того чтобы использовать
clear
, который может нарушать историю команд, рекомендуется позиционировать курсор в верхний левый угол экрана с помощью командыtput cup 0 0
.
- Вместо того чтобы использовать
-
Завершение работы:
- После завершения работы скрипта используйте
tput rmcup
для возвращения терминала к основному экрану и восстановления предыдущего состояния.
- После завершения работы скрипта используйте
Пример:
tput smcup
tput cup 0 0 # устанавливаем курсор в верхний левый угол
# выполнение скрипта
tput rmcup
Использование управляющих последовательностей ANSI
Альтернативный метод основан на использовании управляющих последовательностей ANSI, которые могут быть эффективны в случаях, когда tput
не дает ожидаемого результата:
- Переключение экранов:
echo -e '\e[?1049h'
— это последовательность для перехода на альтернативный экран, которая сохраняет основное состояние экрана и позицию курсора.- После завершения работы скрипта используйте
echo -e '\e[?1049l'
для восстановления оригинального экрана вместе с позицией курсора.
Пример:
#!/usr/bin/env bash
echo -e '\e[?1049h'
# выполнение скрипта
echo -e '\e[?1049l'
Заключение
Оба метода, tput
и управляющие последовательности ANSI, позволяют эффективно переключаться между основным и альтернативным экранами терминала, сохраняя и восстанавливая его состояние после запуска программы. Это полезно для работы с такими приложениями, как vim
, nano
, man
, и ranger
, где важно сохранить предварительное состояние терминала. Важно следить за поддержкой соответствующих функций вашим терминалом, так как не все терминалы поддерживают данные возможности.