Предотвратить мигание текста / экрана при выполнении очистки

11

Мой сценарий делает что-то вроде:

while :;
   clear

   do_a_lot_of_output_here

   sleep 1
done

Есть ли варианты, чтобы экран не мигал, когда я делаю очистку и вывод? Я хочу сделать это как в watchкоманде (но это написано в C). Любые советы?

clear | hexdump -C

00000000  1b 5b 48 1b 5b 32 4a                              |.[H.[2J|
00000007

PS. Я пользуюсь bashтолько.

ravnur
источник
Можете ли вы добавить вывод вашего clear | hexdump -C?
ot--
Я расширил вопрос.
Равнур
Я нашел другое решение на stackoverflow.com/questions/5367068/… - echo -en "\ec"вспышка тоже?
ot--
Я нашел это тоже. Оба варианта из ответа тоже мигают.
Равнур
Я имел успех с этим инструментом: excess.org/article/2009/07/watch1-bash-unicode
Рейн

Ответы:

8

Перепрошивка происходит потому, что скрипт очищает весь экран. Если он закрашивает существующий текст и очищает только при необходимости, мерцание не будет.

Вот пример:

#!/bin/sh
watchit() {
    HOME=$(tput cup 0 0)
    ED=$(tput ed)
    EL=$(tput el)
    ROWS=$(tput lines)
    COLS=$(tput cols)
    printf '%s%s' "$HOME" "$ED"
    while true
    do
        CMD="$@"
        ${SHELL:=sh} -c "$CMD" | head -n $ROWS | while IFS= read LINE; do
            printf '%-*.*s%s\n' $COLS $COLS "$LINE" "$EL"
        done
        printf '%s%s' "$ED" "$HOME"
        sleep 1
    done
}

watchit top -b -n 1

Это делает это:

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

Если вы хотите обработать экран с изменяемым размером, вы можете переместить назначения в ROWSи COLSвнутри внешнего цикла, например,

#!/bin/sh
watchit() {
    HOME=$(tput cup 0 0)
    ED=$(tput ed)
    EL=$(tput el)
    printf '%s%s' "$HOME" "$ED"
    while true
    do
        ROWS=$(tput lines)
        COLS=$(tput cols)
        CMD="$@"
        ${SHELL:=sh} -c "$CMD" | head -n $ROWS | while IFS= read LINE; do
            printf '%-*.*s%s\n' $COLS $COLS "$LINE" "$EL"
        done
        printf '%s%s' "$ED" "$HOME"
        sleep 1
    done
}

watchit top -b -n 1

потому что tputпросит текущий размер экрана из системы.

Дальнейшее чтение:

Томас Дики
источник
1
Для тех, кто использует #!/bin/bashи хочет использовать watchitвстроенные в небольшой автономный скрипт с некоторыми функциями, вы можете сделать export -f function_name; watchit function_name.
aggregate1166877
Это отлично сработало для меня, за исключением того, что я столкнулся с забавными проблемами в верхней части экрана, где вещи будут сдвигаться и сбиваться на более длинные куски текста (что-либо сокращается head). Кажется, проблема заключается в частичной ошибке (по крайней мере, в моей настройке - ssh-сеанс в разделенных панелях, iTerm2) при вычислении количества строк. ROWS=`expr $(tput lines) - 1`обратился к этому отлично.
ohruunuruus
Это замечательно, большое спасибо @ thomas-dickey.
mbarkhau
9

Один из способов предотвратить мигание - получить все выходные данные перед очисткой экрана, чтобы между очисткой и перерисовкой прошло минимальное время. Это похоже на концепцию двойной буферизации:

while :; do
   output=$(do_a_lot_of_output_here)
   clear
   echo "$output"
   sleep 1
done

Это не полностью устраняет мерцание, но в моем опыте это происходит значительно реже.

Кристиан Ромо
источник
Двойная буферизация, как и в любой другой ситуации, защищает день :-)
Ikke
Работал как шарм. Ничего не мерцает :)
Ларс Джуел Дженсен
1
Остальные мерцающий могут быть устранены посредством включения clearв буфер, то есть output=$(clear; do_a_lot_of_output_here).
КДБ
@kdb Хорошее предложение! Это почти полностью исключает мерцание для меня и намного проще, чем принятый ответ.
Майкл Миор
Это работает как шарм! Сначала я пропустил двойные кавычки в 'echo "$ output", а затем все линии запутались. (Все пробелы считаются одиночными.)
всплывающее окно
5

Мигание является неизбежным результатом очистки экрана каждый раз вокруг цикла. Вы можете переместить курсор в верхнюю часть экрана и вместо этого перезаписать части старого вывода.

# You may want to do this if your code is in a script.
unhide_cursor() {
    printf '\e[?25h'
}
trap unhide_cursor EXIT

# Hide the cursor (there is probably a much better way to do this)
printf '\e[?25l'
clear 
while true ; do
    # Move the cursor to the top of the screen but don't clear the screen
    printf '\033[;H' 
    do_a_lot_of_output_here
    sleep 1
done

Этот скрипт оставит артефакты, если ваш вывод уменьшается. Это также не очень вероятно, будет портативным. Я проверял это только с помощью urxvt, xterm и st.


источник
В этом случае курсор начинает переходить к концу вывода из произвольной позиции (и, конечно, артефактов). Также я пытался tput clearс теми же результатами (мигает)
Равнур
Я добавил пару строк в скрипт, чтобы скрыть курсор.
Нет курсора - нет прыжков. Но об артефактах? Выход имеет случайное количество строк. Нужно ли заполнять весь скрипт пробелами перед началом нового вывода? Но, в любом случае, спасибо за ваше время и усилия: +1 от меня за это, но это не решает мою проблему
ravnur
Я нашел только один способ избавиться от всех артефактов: tput ed. Но это вызывает мигание
Равнур
Какой эмулятор терминала вы используете? С tput edправой до do_a_lot...линии, я вижу мигающий в urxvt , но не XTerm или ст.
1

В качестве продолжения ответа Кристиана я сделал следующую функцию bash, которая работает, если окно консоли меньше выходных данных команды:

function watcher()
{
    lines=$(tput lines)
    while true; do
        output="$($@ | head -n $lines)"
        clear
        echo -e "$output"
        sleep 2
    done
}

Это позволяет передавать любую команду наблюдателю. Если вы используете git, используйте git config --global color.status alwaysи затем:

watcher git status

Будет отображать постоянно обновляемый вывод состояния git.

eresonance
источник