Восстановление сеанса после выполнения shell-скрипта

Вопрос или проблема

Из 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:

ANSI Escape Sequences

Эти методы основаны на том, что я там нашел. Бесплатно – мой любимый цвет, и просто – мой любимый метод. Это и то, и другое.

Можно использовать как 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

  1. Подготовка окружения:

    • Используйте tput smcup для переключения на альтернативный экран, который сохраняет текущее состояние терминала.
    • Команда tput предоставляет пользовательский интерфейс к capabilities библиотеки terminfo, что позволяет более гибко управлять терминалом.
  2. Сброс и очистка экрана:

    • Вместо того чтобы использовать clear, который может нарушать историю команд, рекомендуется позиционировать курсор в верхний левый угол экрана с помощью команды tput cup 0 0.
  3. Завершение работы:

    • После завершения работы скрипта используйте tput rmcup для возвращения терминала к основному экрану и восстановления предыдущего состояния.

Пример:

tput smcup
tput cup 0 0  # устанавливаем курсор в верхний левый угол
# выполнение скрипта
tput rmcup

Использование управляющих последовательностей ANSI

Альтернативный метод основан на использовании управляющих последовательностей ANSI, которые могут быть эффективны в случаях, когда tput не дает ожидаемого результата:

  1. Переключение экранов:
    • 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, где важно сохранить предварительное состояние терминала. Важно следить за поддержкой соответствующих функций вашим терминалом, так как не все терминалы поддерживают данные возможности.

Оцените материал
Добавить комментарий

Капча загружается...