Getting Started with Linux: Your First Progress Bar Program Made Easy!

Linux | Red Hat Certified | IT Technology | Operations Engineer

👇 Join our technical exchange QQ group with 1000 members, note 【public account】 for faster approval

Getting Started with Linux: Your First Progress Bar Program Made Easy!

1. Carriage Return and Line Feed

In Windows, we consider carriage return and line feed as one concept; however, in reality, line feed moves to the current position of the next line, while carriage return returns to the beginning of the current line.

We perceive carriage return and line feed as a single concept because when we use \n, it performs both carriage return and line feed operations.

Now let’s look at these two pieces of code in Linux

#include<stdio.h>int main(){     printf("Late grown\n");     return 0;}
#include<stdio.h>int main(){     printf("Late grown\n");     return 0;}

Getting Started with Linux: Your First Progress Bar Program Made Easy!

We can see the difference between \n and \r, but the output results are different, where \r represents carriage return;

So why doesn’t the carriage return \r show the output result?

This is where we need to understand the concept of the buffer.

A buffer, also known as cache, is a part of memory space. In other words, a certain amount of storage space is reserved in memory to buffer input or output data; this reserved space is called a buffer.

We can roughly understand that during input and output, it is not done character by character (with some exceptions), here it is a line buffer.

Line buffering: When input and output occur, the actual I/O operation is executed only when a newline character is encountered. At this point, the characters we input are first stored in the buffer, and only when the Enter key is pressed does the actual I/O operation take place. Typical examples are stdin and stdout.

In line buffering, output is executed only when a newline character is encountered; therefore, with \r carriage return, the output content is stored in the buffer and is only cleared when the program ends.

Now let’s look at two pieces of code

#include<stdio.h>int main(){     printf("Late grown\n");     sleep(3);     return 0;}
int main(){     printf("Late grown");     sleep(3);     return 0;}

The output results are: (what you see)

The first piece of code outputs “Late grown” first, then sleeps for 3 seconds

The second piece of code sleeps for 3 seconds, and only at the end of the program does it output “Late grown”

The difference between these two pieces of code is \n, which also proves the line buffer, where encountering a newline refreshes the buffer content. (At the end of the program, the buffer content will also be refreshed)

You might wonder, since the buffer content is also refreshed after the program ends, why is there no output result when using \r after the program ends?

This is because in Linux, it needs to output command line information, and after the program execution, the cursor is at the beginning of the line, so the command line information overwrites the content to be output.

Can we perform some operations to see the content to be output?

Of course, we can use fflush to flush the buffer (stdout) content.

int main(){     printf("Late grown");     fflush(stdout);     sleep(3);     return 0;}

The output result of the above code

Outputs “Late grown”, then sleeps for 3 seconds, and finally the program ends; the command line information will overwrite the output content.

2. Countdown Program

Having understood carriage return and line feed, let’s create a simple countdown program.

#include<stdio.h>int main(){    int count =10;    while(count)    {        printf("%-2d\r",count);        fflush(stdout);        count--;        sleep(1);    }}

The code is as above, here are some potential issues

Output only overwrites the first number: use %2d/%-2d, we want the number to occupy two character positions.

The program does not display the countdown: use fflush to refresh the buffer content.

3. Progress Bar Program

Before we write our first Linux program—a progress bar, let’s review the makefile.

BIN=progressSRC=$(shell ls *.c)OBJ=$(SRC:.c=.o)CC=gccRM= rm -rf$(BIN):$(OBJ)    $(CC) -o $@ $^%.o:%.c    $(CC) -c $<.PHONY:clean:    $(RM) $(OBJ) $(BIN)

First, shell ls *.c gets all the .c files in the current directory, and you can use wildcard *.c.

SRC:.c=.o changes all .c suffixes in SRC to .o.

$@ and $^, $@ gets the list of dependency files, and $^ gets the target files.

$< gets the dependency files to execute one by one.

Now, let’s officially start writing our first program in Linux—a progress bar.

Progress Bar Program

Getting Started with Linux: Your First Progress Bar Program Made Easy!

We want to see a progress bar that continuously increases, with the percentage rising, and the last box spinning.

How do we achieve this?

First, let’s look at what files we need to write

Getting Started with Linux: Your First Progress Bar Program Made Easy!

Here, we have written four files

makefile file

code.c: the file containing the main function of the program

progress.c: the source code file for the progress bar program

progress.h: the header file for the progress bar program

Having understood these, let’s see how we can implement it?

Although it seems that the progress bar is continuously increasing, in fact, it is just the result of repeated outputs.

Do you remember when we implemented the Snake game, we achieved the movement of the snake by outputting repeatedly; here it is the same, we still print repeatedly to achieve our expected effect.

Output of the progress bar at a certain moment

Let’s first see how the progress bar is output at a certain moment;

Assuming we are downloading a software of size 1024.00MB, and we have currently downloaded 512.00MB, how do we print the progress bar at this moment?

void process(){    double total = 1024.00; // total download size    double current = 512.00; // current download size    int rate = (int)(current*100)/total; // progress bar percentage, also the number of `=` to print    const char* str = "|-/\"; // characters representing the spinning box inside, the last one \ is an escape character    static int cnt =0; // indicates which character to use to spin    char buff[101]={'\0'};    int i=0;    for(i=0;i<rate;i++)    {        buff[i]='=';    }    printf("[%-100s][%d%%][%c]\n",buff,rate,rate[cnt]);}

Getting Started with Linux: Your First Progress Bar Program Made Easy!

We can see that the progress bar at a certain moment has taken shape; now we can implement the complete progress bar based on the code we have already written.

Implementing the Progress Bar

We know how to print the progress bar at a certain moment, now we just need to increment the current progress (this involves a speed issue).

Additionally, how do we make the box inside spin?

To control the speed, we can either control the increment of the current value or control the sleep time using usleep.

To control the spinning of the box, we define a static constant to determine which character should be output.

The specific code is as follows

#include"progress.h"#include<unistd.h>#include<string.h>#define NUM 101#define CH '='void process(){    char buff[NUM];    memset(buff,0,sizeof(buff));    const char* str="|/-\";    int len = strlen(str);    int cnt =0;    while(cnt<=100)    {        printf("[%-100s][%d%%][%c]\r",buff,cnt,str[cnt%len]);        fflush(stdout);        buff[cnt]=CH;        usleep(10000);        cnt++;    }    printf("\n");}

The increment of current and the usleep sleep time can be modified as needed.

For this version, it still feels a bit lacking; if we need to download content of varying sizes and different download speeds, how should we handle it?

This progress bar requires modifying the values within this function to control the speed; we can also modify it to control the total amount through parameters.

Here is the modified version, and I won’t go into detail about the specifics.

Of course, this version also has some shortcomings that need improvement.

code.c

#include"progress.h"#define speed 1.0void Download(double total){    double current=0;    while(current<=total)    {        Process(total,current);        usleep(6000);        current+=speed;    }        printf("\ndownload %lfMB Done\n",total);}int main(){    //process();    Download(1024.00);    return 0;}

progress.h

#include<stdio.h>#include<unistd.h>void Process(double total, double current); 

progress.c

void Process(double total, double current){    char buff[NUM];    memset(buff,0,sizeof(buff));    const char* str = "|/-\";    int len = strlen(str);    // current progress    int num = (int)(current*100)/total;    int i=0;    for(i=0;i<num;i++)    {        buff[i]=CH;    }    static int cnt = 0;    cnt%=len;    printf("[%-100s][%d%%][%c]\r",buff,num,str[cnt]);    cnt++;    fflush(stdout);}

*This concludes the content of this article, I hope it is helpful to you.

For course inquiries, add: HCIE666CCIE

↓ Or scan the QR code below ↓

Getting Started with Linux: Your First Progress Bar Program Made Easy!

What technical points and content would you like to see?

You can leave a message below to let us know!

Leave a Comment