Вопрос

В большинстве современных оболочек вы можете нажимать стрелки вверх и вниз, и в командной строке будут отображаться предыдущие выполненные вами команды. У меня вопрос, как это работает?!

Мне кажется, что оболочка каким-то образом манипулирует stdout, чтобы перезаписать то, что она уже написала?

Я заметил, что такие программы, как wget, также делают это. Кто-нибудь знает, как они это делают?

Это было полезно?

Решение

Он не манипулирует стандартным выводом - он перезаписывает символы, которые уже были отображены терминалом.

Попробуйте это:

#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;
}

Это довольно близко к выводу wget, верно? \r - возврат каретки, который терминал интерпретирует как " переместите курсор назад к началу текущей строки ".

Ваша оболочка, если она bash, использует библиотеку GNU Readline , которая предоставляет гораздо более общая функциональность, включая обнаружение типов терминалов, управление историей, программируемые привязки клавиш и т. д.

Еще одна вещь - когда есть сомнения, источник вашего wget, ваша оболочка и т. д. все доступны.

Другие советы

Чтобы перезаписать текущую стандартную строку вывода (или ее части), используйте \r (или \b.) Специальный символ fflush(stdout); (возврат каретки) вернет курсор в начало строки, позволяя вам переписать это. Специальный символ <=> возвращает каретку только на одну позицию, что позволяет перезаписать последний символ, например

.
#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);

Используйте <=>, потому что стандартный вывод обычно буферизуется , а в противном случае информация не может быть немедленно напечатана на выходе или терминале

В дополнение к \ r и \ b посмотрите на ncurses для некоторых расширенный контроль над тем, что на экране консоли. (Включая столбцы, произвольное перемещение и т. Д.).

Программа, работающая в текстовом терминале / консоли, может манипулировать текстом, отображаемым в ее консоли, различными способами (сделать текст жирным, переместить курсор, очистить экран и т. д.). Это достигается путем печати специальных последовательностей символов, которые называются & Quot; escape-последовательности & Quot; (потому что они обычно начинаются с Escape, ASCII 27).

Если stdout переходит к терминалу, который понимает эти escape-последовательности, отображение терминала соответствующим образом изменится.

Если вы перенаправите стандартный вывод в файл, escape-последовательности появятся в файле (что обычно не то, что вам нужно).

Не существует полного стандарта для escape-последовательностей, но большинство терминалов используют последовательности, представленные VT100 со многими расширениями. Это то, что понимают большинство терминалов в Unix / Linux (xterm, rxvt, konsole) и другие, такие как PuTTY.

На практике вы не будете напрямую жестко кодировать escape-последовательности в своем программном обеспечении (хотя могли бы), а используете библиотеку для их печати, например, ncurses или GNU readline , упомянутые выше. Это обеспечивает совместимость с различными типами терминалов.

Это делается с помощью библиотеки readline ... I ' Я не уверен, как это работает за кулисами, но я не думаю, что это имеет какое-либо отношение к stdout или потокам. Я подозреваю, что readline использует какие-то загадочные (по крайней мере для меня) команды терминала - то есть он взаимодействует с программой терминала, которая фактически отображает ваш сеанс оболочки. Я не знаю, что вы можете получить поведение, похожее на readline, просто распечатав вывод.

(Подумайте об этом: stdout может быть перенаправлен в файл, но трюк клавиш со стрелками вверх / вниз не работает с файлами.)

Вы можете использовать возврат каретки для имитации этого.

#include <stdio.h>

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

    return 0;
}

Программа делает это, печатая специальные символы, которые терминал интерпретирует особым образом. Самая простая версия этого (на большинстве терминалов linux / unix) выводит '\ r' (возврат каретки) в нормальный стандартный вывод, который сбрасывает позицию курсора на первый символ в текущей строке. То, что вы напишете дальше, перезапишет строку, которую вы написали ранее. Это можно использовать, например, для простых индикаторов прогресса.

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

Но есть более сложные последовательности escape-символов, которые интерпретируются по-разному. С этим можно сделать все, например, навести курсор на определенную позицию на экране или установить цвет текста. Если или как эти последовательности символов интерпретируются, зависит от вашего терминала, но общий класс, поддерживаемый большинством терминалов: escape-последовательности ANSI . Поэтому, если вы хотите красный текст, попробуйте:

printf("Text in \033[1;31mred\033[0m\n");

Самый простой способ - это печатать для вывода символа возврата каретки ('\ r').

Курсор будет перемещен в начало строки, что позволит вам перезаписать его содержимое.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top