Evite que el texto / la pantalla parpadee cuando se borra

11

Mi guión hace algo como:

while :;
   clear

   do_a_lot_of_output_here

   sleep 1
done

¿Hay alguna opción para evitar que la pantalla parpadee cuando estoy limpiando y produciendo? Quiero hacerlo como al watchmando (pero está escrito en C). ¿Algún consejo?

clear | hexdump -C

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

PD. Solo estoy usando bash.

ravnur
fuente
¿Puedes agregar la salida de tu clear | hexdump -C?
ott--
He ampliado la pregunta.
ravnur
Encontré otra solución en stackoverflow.com/questions/5367068/… . ¿ echo -en "\ec"Flash también?
ott--
Encontré este también. Ambas variantes de la respuesta parpadean también.
ravnur
Tuve éxito con esta herramienta: exceso.org/article/2009/07/watch1-bash-unicode
raine

Respuestas:

8

El parpadeo se produce porque el script borra toda la pantalla. Si pinta sobre el texto existente y se borra solo según sea necesario, entonces no habrá parpadeo.

Aquí hay un ejemplo:

#!/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

Hace esto:

  • imprime la salida del comando dado que se ajustará en la pantalla (sin ajuste ni desplazamiento)
  • escribe sobre las líneas existentes, borrando la parte de cada línea que no se sobrescribe
  • usa la edcapacidad de su terminal para imprimir desde la ubicación actual hasta el final de la pantalla.

Si desea manejar una pantalla de tamaño variable, puede mover las asignaciones hacia ROWSy COLSdentro del bucle externo, por ejemplo,

#!/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

porque tputpide el tamaño de pantalla actual del sistema.

Otras lecturas:

Thomas Dickey
fuente
1
Para aquellos que usan #!/bin/bashy desean usar watchitincrustado en un pequeño script independiente con algunas funciones, puede hacerlo export -f function_name; watchit function_name.
agregado1166877
Esto funcionó perfectamente para mí, excepto que me encontré con problemas divertidos en la parte superior de la pantalla, donde las cosas cambiaban y se volvían problemáticas por fragmentos de texto más largos (cualquier cosa que se acortara head). Parece que el problema es un error off-by-one (al menos en mi configuración: sesión ssh en paneles divididos, iTerm2) al calcular el número de filas. ROWS=`expr $(tput lines) - 1`Abordó esto perfectamente.
ohruunuruus
Esto es maravilloso, muchas gracias @ thomas-dickey.
mbarkhau
9

Una forma de ayudar a evitar el parpadeo es obtener toda la salida antes de borrar la pantalla para que haya un tiempo mínimo entre borrarla y volver a dibujarla. Esto es similar al concepto de doble búfer:

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

Esto no elimina por completo el parpadeo, pero ocurre con mucha menos frecuencia en mi experiencia.

Cristián Romo
fuente
Doble almacenamiento en búfer, como en cualquier otra situación, salva el día :-)
Ikke
Trabajado como un encanto. No parpadea en absoluto :)
Lars Juel Jensen
1
El parpadeo restante se puede eliminar mediante la inclusión clearen el búfer, es decir output=$(clear; do_a_lot_of_output_here).
kdb
@kdb ¡Buena sugerencia! Esto elimina casi por completo el parpadeo para mí y es mucho más simple que la respuesta aceptada.
Michael Mior
Esto funciona como un encanto! Al principio me perdí las comillas dobles en el 'echo "$ output"', y luego las líneas se confunden. (Todos los espacios en blanco se consideran espacios individuales)
Ventana emergente
5

El parpadeo es un resultado inevitable de borrar la pantalla cada vez que se rodea el bucle. Puede mover el cursor a la parte superior de la pantalla y sobrescribir partes de su salida anterior.

# 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

Este script dejará artefactos si su salida se reduce. Tampoco es muy probable que sea portátil. Solo lo probé con urxvt, xterm y st.


fuente
En este caso, el cursor comienza a saltar al final de la salida desde una posición aleatoria (y artefactos, por supuesto). También lo intenté tput clearcon los mismos resultados (parpadeando)
ravnur
Agregué un par de líneas al script para ocultar el cursor.
Sin cursor, sin saltos. Pero acerca de los artefactos? La salida tiene una cantidad aleatoria de filas. ¿Necesito completar todos los scripts con espacios antes de comenzar una nueva salida? Pero, de todos modos, gracias por su tiempo y esfuerzo: +1 de mi parte por eso, pero no resuelve mi problema
ravnur
Sólo he encontrado una manera de deshacerse de todos los artefactos: tput ed. Pero causa parpadeo
ravnur
¿Qué emulador de terminal estás usando? Con tput edjusto antes de la do_a_lot...línea, veo parpadear en urxvt pero no xterm o st.
1

Como una extensión de la respuesta de Cristian, hice la siguiente función bash que funciona si la ventana de la consola es más pequeña que la salida del comando:

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

Esto le permite pasar cualquier comando al observador. Si está usando git, use git config --global color.status alwaysy luego:

watcher git status

Mostrará una salida de estado de git siempre actualizada.

eresonance
fuente