Zapobiegaj miganiu tekstu / ekranu podczas usuwania

11

Mój skrypt robi coś takiego:

while :;
   clear

   do_a_lot_of_output_here

   sleep 1
done

Czy jest jakaś opcja, aby zapobiec mruganiu ekranu, gdy wykonuję czyszczenie i wyświetlanie? Chcę to zrobić jak w watchpoleceniu (ale jest napisane C). Jakieś porady?

clear | hexdump -C

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

PS. Używam bashtylko.

ravnur
źródło
Czy możesz dodać wynik swojego clear | hexdump -C?
ott--
Rozszerzyłem pytanie.
ravnur
Znalazłem inne rozwiązanie na stackoverflow.com/questions/5367068/… - czy echo -en "\ec"flash też?
ott--
Też to znalazłem. Oba warianty z odpowiedzi też migają.
ravnur
Miałem sukces z tym narzędziem: nadmorski.org/article/2009/07/watch1-bash-unicode
raine

Odpowiedzi:

8

Miganie występuje, ponieważ skrypt czyści cały ekran. Jeśli maluje istniejący tekst i czyści tylko w razie potrzeby, nie będzie migotania.

Oto przykład:

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

Robi to:

  • wypisuje dane wyjściowe z podanego polecenia, które zmieszczą się na ekranie (bez zawijania lub przewijania)
  • zapisuje istniejące linie, usuwając część każdej linii, która nie jest nadpisywana
  • wykorzystuje edmożliwości twojego terminala do drukowania od bieżącej lokalizacji do końca ekranu.

Jeśli chcesz obsłużyć ekran o zmiennym rozmiarze, możesz przenieść zadania do ROWSi COLSwewnątrz zewnętrznej pętli, np.

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

ponieważ tputprosi o bieżący rozmiar ekranu z systemu.

Dalsza lektura:

Thomas Dickey
źródło
1
Dla tych, którzy używają #!/bin/bashi chcą korzystać z watchitosadzonego w małym samodzielnym skrypcie z niektórymi funkcjami, możesz to zrobić export -f function_name; watchit function_name.
aggregate1166877
Działa to idealnie dla mnie, z tym wyjątkiem, że napotkałem zabawne problemy na górze ekranu, w których rzeczy zmieniały się i powodowały usterki w przypadku dłuższych fragmentów tekstu (wszystko ulega skróceniu head). Wydaje się, że problemem jest błąd „jeden po drugim” (przynajmniej w mojej konfiguracji - sesja ssh w podzielonych oknach, iTerm2) podczas obliczania liczby wierszy. ROWS=`expr $(tput lines) - 1`zajął się tym doskonale.
ohruunuruus
To wspaniale, dziękuję bardzo @ thomas-dickey.
mbarkhau
9

Jednym ze sposobów zapobiegania miganiu jest uzyskanie wszystkich danych wyjściowych przed wyczyszczeniem ekranu, tak aby pomiędzy czyszczeniem a przerysowaniem było jak najmniej czasu. Jest to podobne do koncepcji podwójnego buforowania:

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

Nie eliminuje to całkowicie migotania, ale z mojego doświadczenia zdarza się to znacznie rzadziej.

Cristián Romo
źródło
Podwójne buforowanie, jak w każdej innej sytuacji, zabezpiecza dzień :-)
Ikke
Działa jak urok. W ogóle nie migotanie :)
Lars Juel Jensen
1
Pozostałe migotanie można wyeliminować poprzez włączenie cleardo bufora, tj output=$(clear; do_a_lot_of_output_here).
kdb
@kdb Ładna sugestia! To prawie całkowicie eliminuje migotanie i jest o wiele prostsze niż zaakceptowana odpowiedź.
Michael Mior
To działa jak urok! Na początku brakowało mi podwójnych cudzysłowów w „echo” $ output ”, a potem wszystkie linie były zamazane. (Wszystkie białe znaki są traktowane jako pojedyncze spacje).
Popup
5

Miganie jest nieuniknionym rezultatem czyszczenia ekranu za każdym razem wokół pętli. Możesz przesunąć kursor na górę ekranu i zamiast tego zastąpić fragmenty starych wyników.

# 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

Ten skrypt pozostawi artefakty, jeśli twój wynik się zmniejszy. Jest również mało prawdopodobne, aby był przenośny. Testowałem to tylko z urxvt, xterm i st.


źródło
W takim przypadku kursor zaczyna przeskakiwać na koniec wyjścia z losowej pozycji (i oczywiście artefaktów). Próbowałem też tput clearz tymi samymi wynikami (mruganie)
ravnur
Dodałem kilka wierszy do skryptu, aby ukryć kursor.
Bez kursora - bez skoków. Ale o artefaktach? Dane wyjściowe mają losową liczbę wierszy. Czy muszę wypełnić cały skrypt spacjami przed rozpoczęciem nowego wyjścia? Ale w każdym razie dziękuję za twój czas i wysiłki: +1 ode mnie za to, ale to nie rozwiązuje mojego problemu
ravnur
Znalazłem tylko jeden sposób, aby pozbyć się wszystkich przedmiotów: tput ed. Ale powoduje mruganie
ravnur
Jakiego emulatora terminala używasz? Z tput edtuż przed do_a_lot...linią, widzę miga w urxvt ale nie xterm lub ul.
1

Jako rozszerzenie odpowiedzi Cristiana stworzyłem następującą funkcję bash, która działa, jeśli okno konsoli jest mniejsze niż wynik polecenia:

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

To pozwala przekazać dowolne polecenie obserwatorowi. Jeśli używasz git, użyj, git config --global color.status alwaysa następnie:

watcher git status

Wyświetla zawsze aktualizowany wynik statusu git.

eresonans
źródło