命令行进度条实现

原创 2017年06月19日 20:40:21

对于很多包管理工具而言,拥有一个进度条基本上来说是必备的,可以轻松的让用户知道当前程序的进度。
VSCode命令行进度条美化版
命令行下其实也可以活得很精彩。


今天来学一下如何制作一个命令行下的进度条工具。回想之前用Python写过的getpass2,用了标准输出来实现的。和这次的进度条需求相比,感觉应该是一回事。应该就是清空上次的打印内容,然后write本行数据。

单行输出

果不其然,在网上大致搜了一下,发现很多都是使用类似的思路。那既然如此,就没必要重复造轮子了。干脆用人家的好了。

single-line-log

关于这个模块的使用,可以通过npm进行安装,也可以下载源代码,放到本地使用。其核心代码超少,我就直接贴出代码好了。

/**
 * 单行内容输出效果。
 */
const MOVE_LEFT = new Buffer('1b5b3130303044', 'hex').toString();
const MOVE_UP = new Buffer('1b5b3141', 'hex').toString();
const CLEAR_LINE = new Buffer('1b5b304b', 'hex').toString();

/**
 * 借助一个第三方的计算字符串长度的模块。
 */
const stringwidth = require('string-width');

module.exports = function(stream) {
    var write = stream.write;
    var str;

    stream.write = function(data) {
        if(str && data!== str) str = null;
        return write.apply(this, arguments);
    };

    if(stream === process.stderr || stream === process.stdout) {
        process.on('exit', ()=>{
            if(str!==null) stream.write('');
        });
    }

    var prevLineCount = 0;
    var log = function(){
        str = '';
        var nextstr = Array.prototype.join.call(arguments, ' ');

        // 清屏
        for(var i =0; i < prevLineCount; i++) {
            str += MOVE_LEFT + CLEAR_LINE + (i < prevLineCount-1?MOVE_UP:'');
        }

        // 更新实际内容
        str += nextstr;
        stream.write(str);

        // 下行要删除多少行内容
        var prevLines = nextstr.split('\n');
        prevLineCount = 0;
        for(var i = 0; i < prevLines.length; i++) {
            prevLineCount += Math.ceil(stringwidth(prevLines[i]) / stream.columns) || 1;
        }
    };

    log.clear = function() {
        stream.write('');
    };

    return log;
};

/**
 * 对外提供接口。
 */
module.exports.stdout = module.exports(process.stdout);
module.exports.stderr = module.exports(process.stderr);

是的,就只有这么几行的内容。阅读代码就不难发现,就是我上面说的那个思路。

测试输出

接下来简单的进行测试一下。因为对于小文件来说,的一下就完事了,所以干脆找个大文件,比如一个电影来进行测试。

var log = require('./singleline').stdout;
var fs = require('fs');
var strformat = require('str-format');
var read = 0;
var filepath = "E:\\Video\\电影\\大内密探零零发.mkv";
var size = fs.statSync(filepath).size;

var rs = fs.createReadStream(filepath);
rs.on('data', function(data){
    read += data.length;
    var percentage = Math.floor(100*read/size);
    // log("Writing to super large file\n["+percentage+'%]', read, 'bytes readed.')
    log(strformat.format('Writing to super large file.\n[ {}% ] bytes readed.', [percentage]))
});

里面用到了我之前写的一个Python风格的字符串格式化模块。有需要的可以使用

npm install str-format

来进行安装和使用。

下面来看下运行的效果。
单行输出测试,大电影形式

其实这样就实现了一个简单的进度条了。但是为了使其更加通用,接下来对其进行了一个封装。


progressbar

虽然说是进行了封装, 其实重要的还只是percentage那部分的内容了。

封装代码

/**
 * 进度条实现。
 */
const log = require('./singleline').stdout;
const strformat = require('str-format').format;

/**
 * 封装一个进度条工具。
 */
function ProgressBar(description, bar_length) {
    this.description = description || "PROGRESS";
    this.length = bar_length.length || 28;

    // 刷新进度条图案,文字的方法
    this.render = function(opts) {
        var percentage = (opts.completed/opts.total).toFixed(4);
        var cell_num = Math.floor(percentage * this.length);
        // 拼接黑色条
        var cell = '';
        for(var i = 0; i < cell_num; i++) {
            cell += '█';
        }
        // 拼接灰色条
        var empty = '';
        for(var i = 0; i < this.length - cell_num; i++) {
            empty += '░';
        }
        // 拼接最终文本
        var cmdtext = strformat("<{}:{}%> {}{}  [ {}/{} `{}`]", [this.description, (100*percentage).toFixed(2),
        cell, empty, opts.completed, opts.total, opts.status]);
        log(cmdtext);
    };
}

/**
 * 模块导出。
 */
module.exports = ProgressBar;

测试进度条效果

var ProgressBar = require('./progressbar');
var pb = new ProgressBar('下载进度', 50);
var num = 0, total = 100;
function downloading() {
    if (num < total) {
        pb.render({ completed: num, total: total, status: '下载中...' });
        num++;
        setTimeout(function () {
            downloading();
        }, 50);
    }else{
        pb.render({completed: num, total: total, status: "下载完毕."});
            process.exit(0);
    }
}

downloading()

运行效果,如下:
进度条本地测试

至此,一个还算是比较通用的进度条就算是完成了。每次需要手动更新进度的时候,只需要调用render方法即可。

美化

单纯的黑白字符,看起来挺枯燥的,下面使用一个终端彩色模块来对其进行一下美化。

美化版进度条包装

/**
 * 进度条实现。
 */
const log = require('./singleline').stdout;
const strformat = require('str-format').format;
const clicolor = require('cli-color');

/**
 * 封装一个进度条工具。
 */
function ProgressBar(description, bar_length) {
    this.description = description || "PROGRESS";
    this.length = bar_length.length || 28;

    // 刷新进度条图案,文字的方法
    this.render = function(opts) {
        var percentage = (opts.completed/opts.total).toFixed(4);
        var cell_num = Math.floor(percentage * this.length);
        // 拼接黑色条
        var cell = '';
        for(var i = 0; i < cell_num; i++) {
            cell += '█';
        }
        // 拼接灰色条
        var empty = '';
        for(var i = 0; i < this.length - cell_num; i++) {
            empty += '░';
        }

        var percent = (100*percentage).toFixed(2);
        /**
         * 使用cli-color进行包装美化。
         */
        this.description = clicolor.blue.bold(this.description);
        cell = clicolor.green.bgBlack.bold(cell);
        opts.completed = clicolor.yellow.bold(opts.completed);
        opts.total = clicolor.blue.bold(opts.total);
        opts.status = percent==100.00?clicolor.green.bold(opts.status):clicolor.red.bold(opts.status);


        // 拼接最终文本
        var cmdtext = strformat("<{}:{}%> {}{}  [ {}/{} `{}`]", [this.description, percent,
        cell, empty, opts.completed, opts.total, opts.status]);
        log(cmdtext);
    };
}

/**
 * 模块导出。
 */
module.exports = ProgressBar;

美化版进度条测试

var ProgressBar = require('./progressbar');
var pb = new ProgressBar('下载进度', 72);
var num = 0, total = 100;
function downloading() {
    if (num < total) {
        pb.render({ completed: num, total: total, status: '下载中...' });
        num++;
        setTimeout(function () {
            downloading();
        }, 20);
    }else{
        pb.render({completed: num, total: total, status: "下载完毕."});
            process.exit(0);
    }
}

downloading()

下面看下执行结果。

  • 先看下在VSCode下的执行效果。
    VSCode命令行进度条美化版

  • 然后是常规的CMD命令行下效果。
    常规CMD命令行进度条美化版


好了,大致就是这么个内容了。在Linux上还有很多更加优秀的此类实现。有机会再去借鉴借鉴好了。

版权声明:本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。转载时请标注http://blog.csdn.net/marksinoberg.

相关文章推荐

Nginx 从零搭建

前言 准备篇 搭建篇 安装虚拟机 安装Nginx pcre安装 zlib安装 openssl安装 安装Nginx 安装Apache 配置 验证篇 Nginx ServerA ServerB Nginx...

给自己看的Redis

前言 安装 服务器 本地 配置相关 远程连接 修改密码 登录 远程连接 无密码 无密码 密码不正确 密码类型不对 密码登录 总结前言昨天又收到一条来自阿里云安全方面的短信,说是服务器存在对外DDoS攻...

http下载支持命令行进度显示

  • 2015年05月02日 00:23
  • 29KB
  • 下载

仿微信中加载网页时带线行进度条的WebView的实现

为了仿微信中加载网页时带进度条的WebView的实现,首先我们来看一下微信中的效果是什么样的: 明确需求之后,我们来开始动手做,首先我们来自定义一个带进度条的WebView,名字为Progre...

Linux下进度条实现以及缓冲区和回车换行

进度条实现1.首先,为了实现进度条,我编写了一段小的C代码: #include #includeint main() { int i = 0; char b[102]; con...
  • tzy5210
  • tzy5210
  • 2017年02月16日 21:22
  • 134

shell 语法 shell命令 用shell编写进度条

`和()都是命令替换符。由反引号括起来的也是⼀条命令,Shell先执⾏该命令,然后将输出结果⽴刻代换到当前命令⾏中。命令代换也可以⽤()都是命令替换符。 由反引号括起来的也是⼀条命令,Shell先...

SleePLib进度条实现

  • 2016年04月29日 14:25
  • 189KB
  • 下载

用html5和jquery实现圆形进度条

  • 2017年07月30日 16:21
  • 38KB
  • 下载

js实现进度条(不带百分比)

js实现进度条(不带百分比)
  • ZJDWHD
  • ZJDWHD
  • 2016年12月12日 11:54
  • 481
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:命令行进度条实现
举报原因:
原因补充:

(最多只允许输入30个字)