NodeJS脚本在控制台中显示每行的更新

时间:2018-02-01 11:10:06

标签: node.js command-line-interface

我有一个用NodeJS编写的小命令行程序来处理给定目录中的所有文本文件。由于Node是异步的,脚本将读取所有文件,处理它们并输出类似这样的文件

Converting file: example-file-1.txt
Converting file: example-file-2.txt
Converting file: example-file-3.txt
Converting file: example-file-4.txt
File example-file-3.txt converted!
File example-file-1.txt converted!
File example-file-2.txt converted!
File example-file-4.txt converted!

这不是很漂亮,“转换”的消息不是有序的,因为文件大小不同,并且以不同的速度完成处理。
我希望看到的是这样的事情

File example-file-1.txt: DONE! // processing of this file has finished
File example-file-2.txt: converting... // this file is still processing
File example-file-3.txt: DONE!
File example-file-4.txt: converting...

每行的最右边部分应该根据进度动态更新。

我在这里要问的是如何在控制台中显示几行消息,我可以在脚本进行时更新这些消息?

2 个答案:

答案 0 :(得分:2)

端子

使用标准终端功能,只有回车\r,允许将光标重置为当前行的开头并覆盖它以进行更新。

大多数终端都支持ANSI / VT100控制代码,允许设置颜色,光标定位和其他屏幕更新。使用这些的节点模块例如:

  • charm:使用ansi终端字符写入颜色和光标位置。
  • blessed:类似curses的库,带有node.js的高级终端接口API。

Windows支持转义序列after some tweaks

最小示例

以下示例通过直接发送控制命令并写出缓存屏幕来解决任务。使用的命令是:

  1. 通过发送\x1b[H
  2. 将光标重新定位在左上角
  3. 发送\x1b[2J
  4. 清除屏幕

    写出多于可用的行将导致闪烁。

    var progress = {
        'example-file-1.txt': 'converting',
        'example-file-2.txt': 'converting',
        'example-file-3.txt': 'converting'
    }
    
    function renderProgress() {
        // reset cursor, clear screen, do not write a new line
        process.stdout.write('\x1b[H\x1b[2J')
    
        // render progress
        Object.keys(progress).forEach(filename => console.log(`${filename}: ${progress[filename]}`))
    }
    
    function updateProgress(filename, status) {
        progress[filename] = status
        renderProgress()
    }
    
    // render initial progress
    renderProgress()
    
    // simulare updates
    setTimeout(() => updateProgress('example-file-2.txt', 'done'), 1000)
    setTimeout(() => updateProgress('example-file-1.txt', 'reconsidering'), 2500)
    setTimeout(() => updateProgress('example-file-3.txt', 'done'), 4000)
    setTimeout(() => updateProgress('example-file-1.txt', 'done'), 6000)
    

答案 1 :(得分:0)

我最终以同步方式处理我的所有文件,并在回调中使用简单的递归函数。用户Ora库显示终端窗口中每个文件的进度。

var files = [];

// get all "txt" files from a given directory and store them in an array "files"
function getFiles(dir) {
    var filesRaw = fs.readdirSync(dir);
    for (var i in filesRaw) {
        var name = dir === '.' ? filesRaw[i] : dir + '/' + filesRaw[i];
        var ext = path.extname(name);
        if (!fs.statSync(name).isDirectory()) {
            if (ext == '.txt') {
                files.push(path.resolve(process.cwd(), name));
            }
        }
    }
    // only after the directory is read, run the function that starts processing those files
    recode(files);
}

function recode() {
    if (files.length > 0) {
        var fileName = files.shift();
        // this is the terminal spinner library
        const spinner = ora(`File ${path.basename(fileName)} processing...`).start();
        var fileSingle = fs.readFileSync(fileName);
        var data = new Buffer(fileSingle, "ascii");
        var translated = encoding.convert(data, "UTF-8", "CP1250");
        var converted = iconvlite.encode(translated, "utf8").toString();

        fs.writeFile(fileName, converted, function(err) {
            if (err) {
                spinner.fail(`File ${path.basename(fileName)} failed.`);
                console.log(err);
            } else {
                spinner.succeed(`File ${path.basename(fileName)} DONE!`);
            }
            // process another file from the files array only when the previous file is finished and saved to disk
            recode(files);
        });
    } else {
        console.log('No more files to process or all files done.');
    }
}