Jak zmienić zawartość linii na terminalu zamiast pisać nową?

24

Tak więc, gdy wgetpobiera stronę internetową, pokazuje pasek stanu wskazujący, ile plików jest / są pobierane. To wygląda tak:

25%[=============>______________________________________] 25,000 100.0K/s (podkreślenia są spacjami; po prostu nie mogłem wymyślić, jak uzyskać tam więcej niż jedno kolejne pole)

Jednak zamiast pisać kolejną linię na standardowe wyjście i dodawać kolejny pasek postępu, aktualizuje go w następujący sposób:

50%[===========================>________________________] 50,000 100.0K/s

I wgetnie jest to jedyny przykład tego. Na przykład, kiedy wpinasz coś do, lessa następnie kończysz, twój oryginalny monit jest nadal wyświetlany, wraz z wynikiem wszelkich wcześniejszych poleceń. To tak, jakbyś nigdy nie wyszedł.

Tak więc moje pytania brzmią: jak to się nazywa, jak to zaimplementować, czy działa tylko dla pojedynczej linii na raz i czy mogę tego użyć w C?

czterokrotnie
źródło
5
Polecam lekturę BashFAQ 44 . Może Cię to zainteresować.
jw013

Odpowiedzi:

32

Przede wszystkim twoje pytanie nie ma nic wspólnego z bash, ale z terminalem. Terminal odpowiada za wyświetlanie tekstu programów, a sam bash nie ma kontroli nad programami po ich uruchomieniu.

Terminale oferują sekwencje kontrolne do sterowania kolorem, czcionką, pozycją kursora i nie tylko. Lista standardowych sekwencji terminali znajduje się na stronie http://www.termsys.demon.co.uk/vtansi.htm Możesz na przykład

  • ustaw kursor na początku linii
  • usuń linię później
  • napisz nową linię

aby utworzyć pasek postępu.

Bardziej zaawansowane sekwencje specjalne terminali są zazwyczaj zależne od terminali, np. Działają tylko z Eterm lub Xterm. ncurses - to biblioteka programistyczna do tworzenia interaktywnych programów za pomocą terminala, dzięki czemu nie musisz używać sekwencji specjalnych.

Jak zastąpić istniejącą linię sekwencjami końcowymi

echo long text
sleep 1
printf "\033[1A"  # move cursor one line up
printf "\033[K"   # delete till end of line
echo foo

Jak zastąpić istniejącą linię bez sekwencji terminali

Jednym prostym rozwiązaniem jest nie pisanie nowego wiersza na końcu, ale zapisanie znaku powrotu karetki, co w zasadzie resetuje kursor do początku linii, np .:

echo -n first 
sleep 1 
echo -ne "\rsecond"
echo

Znak \rpowrotu karetki umieści kursor na początku wiersza i umożliwi zastąpienie zawartości wiersza.

Przełączaj między buforami, takimi jak lesslubvi

Zachowanie lessjest również spowodowane bardziej zaawansowaną funkcją terminalu, alternatywnym ekranem:

W trybie VT102 istnieją sekwencje specjalne do aktywacji i dezaktywacji alternatywnego bufora ekranu, który ma taki sam rozmiar jak obszar wyświetlania okna. Po aktywacji bieżący ekran jest zapisywany i zastępowany ekranem alternatywnym. Zapisywanie linii przewijanych u góry okna jest wyłączone do momentu przywrócenia normalnego ekranu. Wpis xterm (5) umożliwia edytorowi wizualnemu vi (1) przejście do alternatywnego ekranu edycji i przywrócenie ekranu przy wyjściu. Wyskakujące menu ułatwia przełączanie między normalnym a alternatywnym ekranem do wycinania i wklejania.

http://rosettacode.org/wiki/Terminal_control/Preserve_screen zawiera kilka przykładów, jak to zrobić samemu, albo przez tput , albo za pomocą sekwencji specjalnych.

Ulrich Dangel
źródło
15

Zamiast używać, echoktóry automatycznie dodaje nową linię do łańcucha, użyj printf "%s\r" whatever- powrót karetki przesyła kursor na początek bieżącej linii. przykład:

seq 1 15 | while read num; do printf "%2d\r" $num; sleep 1; done; echo ""
Glenn Jackman
źródło
w zależności od kursora twojego terminala, może być bardziej przyjemneprintf "\r%2d " $num
glenn jackman