Jak nadpisać stdout w C

W większości nowoczesnych powłok, można nacisnąć strzałki w górę iw dół i będzie umieścić, w wierszu polecenia, które zostały wykonane. Moje pytanie brzmi, jak to działa?!

Wydaje mi się, że powłoka w jakiś sposób manipuluje stdout, aby nadpisać to, co już napisał?

Zauważyłem, że programy takie jak wget również to robią. Czy ktoś wie, jak to zrobić?

Author: radbrawler, 2009-03-18

8 answers

To nie manipuluje stdout - to nadpisywanie znaków, które zostały już wyświetlone przez terminal.

Spróbuj tego:

#include <stdio.h>
#include <unistd.h>
static char bar[] = "======================================="
                    "======================================>";
int main() {
    int i;
    for (i = 77; i >= 0; i--) {
        printf("[%s]\r", &bar[i]);
        fflush(stdout);
        sleep(1);
    }
    printf("\n");
    return 0;
}
To blisko wyjścia, prawda? \r to powrót karetki, który terminal interpretuje jako "przesuń kursor z powrotem do początku bieżącej linii".

Twoja powłoka, jeśli jest bash, używa biblioteki GNU Readline , która zapewnia znacznie bardziej ogólną funkcjonalność, w tym wykrywanie typów terminali, historię zarządzanie, programowalne wiązania klawiszy itp.

Jeszcze jedno-w razie wątpliwości, źródło twojego wget, Twojej powłoki itp. wszystkie są dostępne.

 41
Author: ephemient,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-18 00:18:13

Aby nadpisać bieżącą standardową linię wyjściową (lub jej części) użyj \r (lub \b.) Znak specjalny \r (powrót karetki) zwróci karetkę na początek linii, umożliwiając jej nadpisanie. Znak specjalny \b przywróci karetkę tylko o jedną pozycję, umożliwiając nadpisanie ostatniego znaku, np.

#include <stdio.h>
#include <unistd.h>

int i;
const char progress[] = "|/-\\";

for (i = 0; i < 100; i += 10) {
  printf("Processing: %3d%%\r",i); /* \r returns the caret to the line start */
  fflush(stdout);
  sleep(1);
}
printf("\n"); /* goes to the next line */
fflush(stdout);

printf("Processing: ");
for (i = 0; i < 100; i += 10) {
  printf("%c\b", progress[(i/10)%sizeof(progress)]); /* \b goes one back */
  fflush(stdout);
  sleep(1);
}
printf("\n"); /* goes to the next line */
fflush(stdout);

Użyj fflush(stdout); ponieważ standardowe wyjście jest zwykle buforowane i informacje nie mogą być natychmiast wydrukowane na wyjście lub terminal

 22
Author: vladr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-30 12:51:28

Oprócz \r i \b, spójrz na ncurses, aby uzyskać zaawansowaną kontrolę nad tym, co znajduje się na ekranie konsoli. (W tym kolumny, poruszanie się dowolnie itp.).

 11
Author: SoapBox,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-18 00:20:38

Program działający w terminalu tekstowym / konsoli może manipulować tekstem wyświetlanym w konsoli na różne sposoby (pogrubienie tekstu, przesunięcie kursora, wyczyszczenie ekranu itp.). Jest to osiągane przez drukowanie specjalnych sekwencji znaków, zwanych "sekwencjami ucieczki" (ponieważ zwykle zaczynają się od Escape, ASCII 27).

Jeśli stdout trafi do terminala, który rozumie te sekwencje specjalne, wyświetlanie terminala zmieni się odpowiednio.

Jeśli przekierujesz stdout do pliku, sekwencje specjalne pojawią się w pliku (co zwykle nie jest tym, czego chcesz).

Nie ma kompletnego standardu dla sekwencji specjalnych, ale większość terminali używa sekwencji wprowadzonych przez VT100 , z wieloma rozszerzeniami. To jest to, co rozumie większość terminali pod Unix/Linux (xterm, rxvt, konsole) i innych, takich jak PuTTY.

W praktyce nie można bezpośrednio kodować sekwencji specjalnych na twardo do oprogramowania( choć można), ale użyć biblioteki do ich wydrukowania, na przykład ncurses lub GNU readline wymienione powyżej. Pozwala to na kompatybilność z różnymi typami terminali.

 5
Author: sleske,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-18 01:03:14

Robi się to za pomocą biblioteki readline ... Nie jestem pewien, jak to działa za kulisami, ale myślę, że nie ma to nic wspólnego ze stdout lub streamami. Podejrzewam, że readline używa jakiegoś tajemniczego (przynajmniej dla mnie) polecenia terminala-to znaczy współpracuje z programem terminalowym, który faktycznie wyświetla sesję powłoki. Nie wiem, czy można uzyskać zachowanie readline po prostu wydrukować wyjście.

(pomyśl o tym: stdout można przekierować do pliku, ale up/down-klawisze strzałek nie działają na plikach.)

 2
Author: David Z,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-18 00:14:02

Możesz użyć powrotu karetki, aby to zasymulować.

#include <stdio.h>

int main(int argc, char* argv[])
{
    while(1)
    {
        printf("***********");
        fflush(stdout);
        sleep(1);
        printf("\r");
        printf("...........");
        sleep(1);
    }

    return 0;
}
 1
Author: Kknd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-18 00:17:50

Program robi to poprzez drukowanie znaków specjalnych, które terminal interpretuje w specjalny sposób. Najprostszą wersją tego jest (na większości terminali linux/unix) wydrukowanie '\r' (powrót karetki) do normalnego wyjścia, które zresetuje pozycję kursora do pierwszego znaku w bieżącej linii. Więc to, co napiszesz dalej, nadpisze linię, którą napisałeś wcześniej. Można to wykorzystać na przykład do prostych wskaźników postępu.

int i = 0;
while (something) {
  i++;
  printf("\rprocessing line %i...", i);
  ...
}

Ale są bardziej skomplikowane ucieczki sekwencje znaków, które są interpretowane na różne sposoby. Dzięki temu można wykonywać różne czynności, takie jak pozycjonowanie kursora w określonej pozycji na ekranie lub Ustawianie koloru tekstu. To, czy lub jak te sekwencje znaków są interpretowane, zależy od twojego terminala, ale typową klasą obsługiwaną przez większość terminali są sekwencje escape ANSI . Więc jeśli chcesz czerwony tekst, spróbuj:

printf("Text in \033[1;31mred\033[0m\n");
 1
Author: sth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-18 04:07:46

Najprostszym sposobem jest wypisanie na stdout znaku powrotu karetki ('\r').

Kursor zostanie przesunięty na początek wiersza, co pozwoli na nadpisanie jego zawartości.

 0
Author: morais,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-18 00:21:16