当前位置: 首页 > article >正文

【Linux学习】(8)第一个Linux编程进度条程序|git三板斧

前言

  1. 第一个Linux编程——进度条
  2. git的简单使用

一、第一个Linux编程——进度条

在写进度条之前我们需要两个基础知识:

  1. 回车换行
  2. 缓冲区

1. 回车换行

  • 首先我们需要知道回车换行它是两个概念,回车是回车,换行是换行
  • 换行:光标从上往下,直接到下一行(例如光标现在在当前行的第5个位置,换行之后光标直接挪到下一行的第5个位置,并没有在下一行的最左侧)
    回车:把光标挪到当前行的最开始(最左侧),还在当前行
  • 换行一般不单独使用,一般换行回车一起用,所以在C语言中用\n表示换行(注意:\n是换行+回车)
  • 回车,我们用转义字符\r表示

2. 行缓冲区

(1)引入行缓冲区

在讲解之间我们先来学习一个系统函数——sleep

  • 我们通过man手册,查看sleep的使用在这里插入图片描述
  • 调用sleep,休眠指定的秒数

现在我们来看下面的两个代码:

两段代码唯一的区别就是代码1打印完之后换行,代码2打印完之后不换行,如下图:

在这里插入图片描述
分别编译运行这两段代码,运行结果是怎么样的呢?

  • 代码1:先打印hello linux!和换行,随后休眠2秒,结束运行
  • 代码2:先休眠2秒,随后打印hello linux!,结束运行

问题:我们学过C语言的都知道,C代码一定是按照顺序从上往下执行的,无论是代码1还是代码2都应该是先执行A处再执行B处,但是为什么代码2的运行效果好像是先执行了B处的代码,随后才执行的A处?

从C语言的执行顺序,我们知道一定是先执行printf,随后再执行sleep

在sleep的时候,printf已经执行了,只不过信息没有打印出来

那在我sleep的时候,信息“hello linux!”在哪里?——他一定是被保存起来了!——保存在缓冲区

缓冲区就是由C语言维护的一段内存!

缓冲区的刷新策略:行刷新

  • 行刷新:缓冲区他要看你输出的字符串是不是一个完整行
    • 如果是一个完整行,缓冲区就立刻将存在缓冲区的内容刷新出来
    • 如果不是一个完整行,缓冲区就不刷新,直到程序退出或者遇到换行符才将存在缓冲区的内容刷新出来
    • 完整行的标准:是否有‘\n’换行符

了解了缓冲区和缓冲区的刷新策略,现在我们就知道为什么代码2会先休眠再输出了,因为代码2没有‘\n’换行符,不是一个完整行,所以直到程序退出才从缓冲区刷新出来

(2)怎样强制刷新缓冲区

我们先来认识两个知识点:

  1. C程序默认会帮我们打开三个输入输出流,分别叫做标准输入、标准输出、标准错误。今天我们只考虑标准输出。

    C语言默认会帮我们打开三个输入输出文件,输出文件就是显示器,显示器文件对应C语言的stdout。
#include<stdio.h>

   extern FILE *stdin;
   extern FILE *stdout;
   extern FILE *stderr;
  1. 使用fflush刷新文件流,即使缓冲区不是一个完整行,也可以使用fflush强制将缓冲区中的内容马上刷新出来
    在这里插入图片描述

(3)倒计时

在实现进度条之前,我们可以先来实现一个简易的倒计时程序。

实现倒计时需要注意以下几个问题:

  1. 倒计时需要将之前的数字覆盖掉
    • 光标不能向后移动——》解决方案:回车‘\r’
    • 光标不能换行且要立马将缓冲区数据刷新出来——》fflush
  2. 不想让最后的结果被bash覆盖——》在程序的最后换行
  3. 从大到小倒计时,随着倒计时的运行会导致不能完全覆盖之前的数字,例如9覆盖10,反而变成90——》-numd%:左对齐num个字符
#include<stdio.h>
#include<unistd.h>

int main()
{
    int cnt = 10;
    while(cnt >= 0)
    {
     	printf("%-2d\r", cnt);
        fflush(stdout);
        sleep(1);
        cnt--;
    }
    printf("\n");

   //printf("hello linux!"); //A
   //fflush(stdout);         //强制刷新文件流,将缓冲区中的内容马上刷新出来
  //  sleep(2);                //B
    return 0;
}

3. 进度条

(1)准备工作

  1. 首先mkdir processbar创建processbar目录,用于保存进度条代码
  2. 在processbar目录下,touch processbar.c processbar.h main.c三个文件,分别用于进度条的实现、进度条的声明、进度条的调用
  3. 再touch Makefile,用于项目的自动化构建

(2)实现简单的进度条样式

  • 编写Makefile文件,通过make命令生成可执行程序和清理项目
processbar:processBar.c main.c # 可执行目标文件依赖多个源文件的话,使用空格分隔即可
	gcc $^ -o $@

.PHONY:clean
clean:
	rm -f processbar
  • processbar.h:进度条声明
//防止头文件被重复包含
#pragma once

//声明常用的头文件
#include<stdio.h>
#include<string.h>
#include<unistd.h>
//声明函数
extern void processbar(int speed);
//进度条的长度是一个常量,所以我们将其定义为一个宏
#define NUM 101//进度条长度为100,但因为C字符串以'\0'结尾,所以将其定义为101
//进度条的主体字符格式也是一个常量,所以我们也将其定义为一个宏
#define BODY '='
//进度条推进是右边有一个字符'>'表示在推进
#define RIGHT '>'
  • processbar.c:进度条样式的实现

1、如何实现进度条推进的效果

答:循环——通过每一次多输出一个字符,并将前一次输出的字符覆盖,这样不断循环,从左向右依次增加,实现进度条推进的效果

2、进度条的样式

  • 预留100的长度,并左对齐——%-100s
  • 显示进度条推进的进度,即百分比(注意‘%’是一个特殊字符,一般通过两个‘%%’才能输出)
  • 进度条工作时字符’|'顺时针旋转表示在工作
#include"processBar.h"

//进度条工作时字符'|'顺时针旋转表示在工作
const char* lable = "|/-\\"; //'\'是特殊字符
//实现进度条推进的样式,参数speed—由调用者决定每一次推进的速度
void processbar(int speed)
{
    //定义进度条
    char bar[NUM];
    //初始化进度条
    memset(bar, '\0', sizeof(bar));
    int len = strlen(lable);
    //循环:通过每一次多输出一个字符,并将前一次输出的字符覆盖,这样不断循环实现进度条推进的效果
    //进度条长度为NUM-1,从长度为0开始循环,需要循环NUM次—》[0,NUM-1]
    int cnt = 0;//计数器
    while(cnt <= NUM - 1)
    {
        //这里我们不使用'\n',使用'\r'回车将前一次的进度条覆盖掉
        printf("[%-100s][%d%%][%c]\r", bar, cnt, lable[cnt%len]);
        //没有'\n',缓冲区就没有立即刷新,因为显示器默认行刷新
        //所以这里我们使用fflush函数立马把缓冲区的内容刷新出来
        fflush(stdout);
        //每一次输出休眠0.1秒——usleep
        usleep(speed);
        //迭代:每一次比上一次多一个字符
         bar[cnt++] = BODY;
         //注意:临界情况,即当cnt=NUM-1时就不能再执行了
         if(cnt < NUM - 1)
         {
            bar[cnt] = RIGHT;
         }      
    }
    //进度条打印完之后,为了不让bash命令行把我们的进度条覆盖掉,所以这里换行
    printf("\n");
}

  • main.c:进度条样式的调用(测试)
#include"processBar.h"

int main()
{
    //每一次推进,休眠0.1秒
    processbar(100000);
    return 0;
}

(3)进度条调用的场景

  1. 在实际中进度条的进度不应该有我们循环模拟实现,而是由调用者决定的
  2. 在实际中进度条调用时,调用者进行着某种下载任务,每下载1%就调用进度条打印一次进度,不断重复下载,直到下载任务完成
  • processbar.c:进度条样式的实现
//进度条工作时字符'|'顺时针旋转表示在工作
const char* lable = "|/-\\"; //'\'是特殊字符
//定义进度条
char bar[NUM];
//参数rate为进度条的进度
//processbar每一次调用进度+1
void processbar(int rate)
{
    //当进度条小于0和大于100时,直接退出
    if(rate < 0 || rate > 100) return;
    int len = strlen(lable);
    printf("[%-100s][%d%%][%c]\r", bar, rate, lable[rate%len]);
    fflush(stdout);
    bar[rate++] = BODY;
    //注意:临界情况,即当rate=NUM-1时就不能再执行了
    if(rate < NUM - 1) bar[rate] = RIGHT;
}
//下载完毕之后调用initbar初始化进度条
void initbar()
{
    memset(bar,'\0',sizeof(bar));
}

  • main.c:进度条样式的调用(测试)
#include"processBar.h"

typedef void (*callback_t)(int);//函数指针类型
//模拟一种安装或下载
void downLoad(callback_t cb)
{
    int total = 1000;//需要下载1000MB
    int curr = 0;//一开始还没下载为0MB
    //当curr>total即下载完成
    while(curr <= total)
    {
        //进行某种下载任务
        usleep(10000);//模拟下载的时间
        int rate = curr*100/total;//更新进度
        cb(rate);//通过回调,展示进度
        curr += 10;//迭代:循环一次下载10M
    }
    printf("\n");
}
int main()
{

    downLoad(processbar);
    initbar();
    downLoad(processbar);
    initbar();
    downLoad(processbar);
    initbar();
    downLoad(processbar);
    initbar();
    return 0;
}

二、简单使用git

1. git是什么

  1. git是一个版本控制器
  2. git的核心工作主要是两个:
    • 使用git来进行版本管理
    • 使用git多人协作开发
  3. git是client和Server一体的(例如:本地仓库可以传到远端,远端仓库也可以拉取到本地)
  4. git是一个开源的软件,基于git搞了一些商业版的网站——github&&gitee
  • 理解什么是版本管理?

例如:在写毕业论文的时候,我们需要不断的修改,在修改的时候就会产生不同的版本,这个时候我们需要将其保存在同一个文件夹下,也可将这个文件夹称作仓库。这个仓库就是做毕业论文的版本管理的!

  • 理解git

我们怕电脑中的本地仓库被我们弄丢,所以我们可以将其传到远程仓库git中!

2. git的简单使用

在这里我们只学习git的简单使用,把本地仓库的代码传上远端仓库即可

(1)新建仓库

在菜单栏右侧有一个‘+’,新建仓库:

在这里插入图片描述

现阶段我们创建的仓库,填写好仓库名和仓库介绍即可

在这里插入图片描述

  • tip:.gitignore文件是筛选从本地上传到远端仓库的文件的,只要在.gitignore文件有的文件就不能上传到远端仓库!在这里插入图片描述

(2)将远端仓库拉取到本地

  • 先将仓库的url复制一份:
    在这里插入图片描述
    在这里插入图片描述
  • 将远端仓库拉取到本地:

注意:在拉取之前先检测Linux是否下载了git

git --help#检测git是否安装好,简写为--h也可以
git --version#也可检查是否安装好

出现如下图所示,即安装好了

在这里插入图片描述
如果没有安装,粘贴如下命令安装:

sudo yum install git

将远端仓库拉取到本地,指令如下:

git clone [url]#这里的url就是刚在远端仓库复制的url

在这里插入图片描述

(3)本地仓库

  • 我们需要把本地仓库的代码上传到远端仓库,那本地仓库在哪里呢?

本地仓库 .git 在我们拉取到本地的目录中,注意它隐藏的

在这里插入图片描述

  • 为什么我们的本地仓库是隐藏的——就是因为它不想让我们对其修改

(4)三板斧第一招:git add

  • 先把我们要上传的文件拷贝到刚才拉取的克隆本地当中
    在这里插入图片描述
  • 注意:这个文件虽然拷贝到了本地当中,但是它还没有被仓库管理起来
  • 所以git add第一板斧将新拷贝到本地的文件添加到本地仓库
git add [文件名]

在这里插入图片描述

(5)三板斧第二招:git commit

  • 其实git add只是将其添加到仓库的暂存区里了,还需要git commit第二板斧将其真正提交到本地仓库中!
  • git commit提交的时候还应该注明提交日志,描述改动的详细内容
  • 注意:提交日志不能乱写,它是给我们查看的——即通过日志了解提交的文件具体做了什么事
git commit -m "提交日志信息"
  • 首次使用git commit的时候,可能会报错,因为Git需要知道你的用户名和电子邮件地址,以便在每次提交时记录这些信息,但你还没有设置全局的用户名和邮箱。(其本质是为了对代码进行溯源)
    在这里插入图片描述
  • 完成设置后,你应该能够成功地进行Git提交了。再次尝试运行你的提交命令
    在这里插入图片描述
    tip:如果这里设置的邮箱与你git上的邮箱不一致,可能会导致在Gitee没有提交的小绿点。

(6)三板斧第三招:git push

  • 将本地仓库同步到远端仓库(即远端服务器)上
git push
  • push时需要填写用户名和密码
    在这里插入图片描述
  • 同步成功后,刷新Gitee就可以看到你提交的文件了
    在这里插入图片描述

(7)补充:git log&&git status

  • git log:查看历史提交记录
    在这里插入图片描述
  • git status:查看提交状态
    在这里插入图片描述
    到这里我们就学完了git的基本使用

http://www.kler.cn/a/369675.html

相关文章:

  • 深度学习:YOLO v1网络架构、损失值及NMS极大值抑制
  • 在浏览器和Node.js环境中使用Puppeteer的Rollup与Webpack打包指南
  • TCP 攻击为何在 DDoS 攻击中如此常见
  • Spring《声明式事务》
  • 使用Angular构建动态Web应用
  • Java实现邮箱发送邮件添加定时任务(二)
  • 静态局部变量
  • 深入RAG:知识密集型NLP任务的解决方案
  • 路由守卫重定向页面
  • vxe-table 表格中使用输入框、整数限制、小数限制,单元格渲染数值输入框
  • 雷军救WPS“三次”,WPS注入新生力量,不再“抄袭”微软
  • Kubernetes(K8S) + Harbor + Ingress 部署 SpringBoot + Vue 前后端分离项目
  • WPF+MVVM案例实战(一)- 设备状态LED灯变化实现
  • 【Rust练习】18.特征 Trait
  • Puppeteer 与浏览器版本兼容性:自动化测试的最佳实践
  • Javaee---多线程(一)
  • ubuntu系统
  • 重写(外壳不变)
  • Linux下的文件系统(进程与文件)
  • Spring Cloud Alibaba实战入门之Nacos注册中心(四)
  • 青少年编程与数学 02-002 Sql Server 数据库应用 16课题、安全机制
  • HardLockUp
  • Rust 力扣 - 5. 最长回文子串
  • [ vulnhub靶机通关篇 ] 渗透测试综合靶场 DC-7 通关详解 (附靶机搭建教程)
  • PostgreSQL的奥秘:从Read-through到Write-around的缓存机制
  • 什么是服务器?服务器与客户端的关系?本地方访问不了网址与服务器访问不了是什么意思?有何区别