Empêchez le texte / l'écran de clignoter lorsque vous effacez

11

Mon script fait quelque chose comme:

while :;
   clear

   do_a_lot_of_output_here

   sleep 1
done

Y a-t-il des options pour empêcher l'écran de clignoter lorsque je fais de la suppression et de la sortie? Je veux le faire comme dans la watchcommande (mais c'est écrit C). Des conseils?

clear | hexdump -C

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

PS. J'utilise bashseulement.

ravnur
la source
Pouvez-vous ajouter la sortie de votre clear | hexdump -C?
ott--
J'ai élargi la question.
ravnur
J'ai trouvé une autre solution sur stackoverflow.com/questions/5367068/… - echo -en "\ec"clignote-t-il aussi?
ott--
J'ai trouvé celui-ci aussi. Les deux variantes de la réponse clignotent également.
ravnur
J'ai eu du succès avec cet outil: excess.org/article/2009/07/watch1-bash-unicode
raine

Réponses:

8

Le clignotement se produit car le script efface tout l'écran. S'il peint sur le texte existant et ne s'efface que si nécessaire, alors il n'y aura pas de scintillement.

Voici un exemple:

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

Il fait ceci:

  • imprime la sortie de la commande donnée qui tiendra sur l'écran (pas d'habillage ni de défilement)
  • écrit sur les lignes existantes, effaçant la partie de chaque ligne qui n'est pas écrasée
  • utilise la edcapacité de votre terminal pour imprimer depuis l'emplacement actuel jusqu'à la fin de l'écran.

Si vous souhaitez gérer un écran redimensionnable, vous pouvez déplacer les affectations vers ROWSet COLSà l'intérieur de la boucle externe, par exemple,

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

car tputdemande la taille d'écran actuelle du système.

Lectures complémentaires:

Thomas Dickey
la source
1
Pour ceux qui utilisent #!/bin/bashet souhaitent utiliser watchitintégré dans un petit script autonome avec certaines fonctions, vous pouvez le faire export -f function_name; watchit function_name.
aggregate1166877
Cela a parfaitement fonctionné pour moi, sauf que j'ai rencontré des problèmes amusants en haut de l'écran, où les choses bougeaient et devenaient glitch pour des morceaux de texte plus longs (tout ce qui se raccourcissait head). Il semble que le problème soit une erreur ponctuelle (au moins dans ma configuration - session ssh dans les volets fractionnés, iTerm2) lors du calcul du nombre de lignes. ROWS=`expr $(tput lines) - 1`abordé parfaitement.
ohruunuruus
C'est merveilleux, merci beaucoup @ thomas-dickey.
mbarkhau
9

Une façon d'empêcher le flashage est d'obtenir toute la sortie avant d'effacer l'écran afin qu'il y ait un minimum de temps entre l'effacement et le redessinage. Ceci est similaire au concept de double tampon:

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

Cela n'élimine pas complètement le scintillement, mais cela se produit beaucoup moins fréquemment selon mon expérience.

Cristián Romo
la source
La double mise en mémoire tampon, comme dans toute autre situation, protège la journée :-)
Ikke
A fonctionné comme un charme. Pas de scintillement du tout :)
Lars Juel Jensen
1
Le scintillement restant peut être éliminé en incluant le cleardans le tampon, c.-à-d output=$(clear; do_a_lot_of_output_here).
kdb
@kdb Belle suggestion! Cela élimine presque complètement le scintillement pour moi et est beaucoup plus simple que la réponse acceptée.
Michael Mior
Ça fonctionne super bien! Au début, j'ai raté les guillemets doubles dans l '"écho" $ sortie "', puis les lignes se sont embrouillées. (Tous les espaces blancs sont considérés comme des espaces simples.)
Popup
5

Le clignotement est un résultat inévitable de l'effacement de l'écran à chaque fois autour de la boucle. Vous pouvez déplacer le curseur vers le haut de l'écran et remplacer à la place des parties de votre ancienne sortie.

# 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

Ce script laissera des artefacts si votre sortie diminue. Il est également peu probable qu'il soit portable. Je ne l'ai testé qu'avec urxvt, xterm et st.


la source
Dans ce cas, le curseur commence à sauter à la fin de la sortie à partir d'une position aléatoire (et des artefacts bien sûr). J'ai aussi essayé tput clearavec les mêmes résultats (clignotant)
ravnur
J'ai ajouté quelques lignes au script pour masquer le curseur.
Pas de curseur - pas de saut. Mais à propos des artefacts? La sortie a un nombre aléatoire de lignes. Dois-je remplir tous les scripts avec des espaces avant de commencer une nouvelle sortie? Mais, de toute façon, merci pour votre temps et vos efforts: +1 de ma part pour cela mais cela ne résout pas mon problème
ravnur
J'ai trouvé qu'un seul moyen de se débarrasser de tous les artefacts: tput ed. Mais ça fait clignoter
ravnur
Quel émulateur de terminal utilisez-vous? Avec tput edjuste avant la do_a_lot...ligne, je vois clignoter en urxvt mais pas xterm ou st.
1

Dans le prolongement de la réponse de Cristian, j'ai créé la fonction bash suivante qui fonctionne si la fenêtre de la console est plus petite que la sortie de la commande:

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

Cela vous permet de passer n'importe quelle commande à l'observateur. Si vous utilisez git, utilisez git config --global color.status alwayspuis:

watcher git status

Affiche une sortie d'état git toujours à jour.

résonance
la source