基于ANSI转义序列来构建命令行工具

栏目: JavaScript · 发布时间: 5年前

内容简介:命令行工具在输出时,如果是简单的进度更新,可以使用已有不少成熟的命令行工具库,如

命令行 工具 在输出时,如果是简单的进度更新,可以使用 \r\b 来达成刷新行的效果,但如果要更复杂些的如字体颜色、背景颜色、光标位置移动等功能,那就需要使用 ANSI 转移序列了。

已有不少成熟的命令行工具库,如 ReadlineJLinePython Prompt Toolkit ,基于这些库创造了如 mycliipython 等好用的工具。

ANSI 转义序列 有比较悠久的历史,不同平台支持的功能不完全一致,这里学习到的是比较简单常用的,包括字体颜色、背景色和其它装饰的富文本和光标操作。

\u001b 即 ESC 的 ASCII 码, \u001b[0m 是清除之前的设定。

适用于 *nix 的系统。

富文本

前景色

8 色

\u001b[?m ,其中 ? ∈ [30, 37]

\u001b[30m
\u001b[31m
\u001b[32m
\u001b[33m
\u001b[34m
\u001b[35m
\u001b[36m
\u001b[37m
\u001b[0m

16 色

在 8 色的基础上对字体加粗,颜色加亮,得到另外 8 种,加起来就是 16 色。

\u001b[?;1m ,其中 ? ∈ [30, 37]

\u001b[30;1m
\u001b[31;1m
\u001b[32;1m
\u001b[33;1m
\u001b[34;1m
\u001b[35;1m
\u001b[36;1m
\u001b[37;1m
const out = process.stdout;

function colors8(pre, post, startCode = 30) {
  const codePointA = 'A'.codePointAt(0);

  let i = 0;
  while (i < 8) {
    const colorCode = startCode + i;
    const char = String.fromCodePoint(codePointA + i);
    out.write(`${pre}${colorCode}${post}${char} `);
    i++;
  }
  console.log('\u001b[0m');
}

function fgColors8() {
  colors8('\u001b[', 'm');
}

function fgColors8Bright() {
  colors8('\u001b[', ';1m');
}

fgColors8();
fgColors8Bright();

基于ANSI转义序列来构建命令行工具

256 色

\u001b[38;5;?m ,其中 ? ∈ [0, 255]

function colors256(pre, post) {
  let i = 0;
  while (i < 16) {
    let j = 0;
    while (j < 16) {
      const colorCode = i * 16 + j;
      const text = `${colorCode}`.padEnd(4);
      out.write(`${pre}${colorCode}${post}${text}`);
      j++;
    }
    console.log('\u001b[0m');
    i++;
  }
}

function fgColors256() {
  colors256('\u001b[38;5;', 'm');
}

fgColors256();

基于ANSI转义序列来构建命令行工具

背景色

背景色和前景色的方案一致,只是 code 不同而已。

8 色

\u001b[?m ,其中 ? ∈ [40, 47]

\u001b[40m
\u001b[41m
\u001b[42m
\u001b[43m
\u001b[44m
\u001b[45m
\u001b[46m
\u001b[47m

16 色

\u001b[?;1m ,其中 ? ∈ [40, 47]

\u001b[40;1m
\u001b[41;1m
\u001b[42;1m
\u001b[43;1m
\u001b[44;1m
\u001b[45;1m
\u001b[46;1m
\u001b[47;1m
function bgColors8() {
  colors8('\u001b[', 'm', 40);
}

function bgColors8Bright() {
  colors8('\u001b[', ';1m', 40);
}

bgColors8();
bgColors8Bright();

基于ANSI转义序列来构建命令行工具

256 色

\u001b[48;5;?m ,其中 ? ∈ [0, 255]

function bgColors256() {
  colors256('\u001b[48;5;', 'm');
}

bgColors256();

基于ANSI转义序列来构建命令行工具

装饰

\u001b[1m
\u001b[2m
\u001b[3m
\u001b[4m
\u001b[7m

可以单独使用,也可以组合使用(如 \u001b[1m\u001b[4m\u001b[7m 表示加粗、下划线和反色)。

还可以和颜色结合一起使用,如 \u001b[1m\u001b[4m\u001b[44m\u001b[31m Blue Background Red Color Bold Underline

function decorations() {
  const codes = [
    [1, 'High'],
    [2, 'Low'],
    [3, 'Italic'],
    [4, 'Underline'],
    [7, 'Reverse']
  ];

  for (let c of codes) {
    out.write(`\u001b[${c[0]}m${c[1]} \u001b[0m`);
  }
  console.log();
  console.log('\u001b[1m\u001b[4m\u001b[44m\u001b[31mBlue Background Red Color Bold Underline\u001b[0m');
}

decorations();

基于ANSI转义序列来构建命令行工具

光标操作

  • 上: \u001b[{n}A ,光标上移 n
  • 下: \u001b[{n}B ,光标下移 n
  • 右: \u001b[{n}C ,光标右移 n 个位置
  • 左: \u001b[{n}D ,光标左移 n 个位置
  • 下几行行首: \u001b[{n}E
  • 上几行行首: \u001b[{n}F
  • 指定列: \u001b[{n}G
  • 指定位置: \u001b[{n};{m}H ,移动光标到 nm
  • 清屏: \u001b[{n}J
    n=0
    n=1
    n=2
    
  • 清行: \u001b[{n}K
    n=0
    n=1
    n=2
    
function sleep(ms) {
  return new Promise(resolve => {
    setTimeout(resolve, ms);
  });
}

async function progressIndicator() {
  console.log('Loading...');
  let i = 0;
  while(i <= 100) {
    await sleep(10);
    out.write(`\u001b[1000D${i}%`);
    i++;
  }
  // 清除进度信息(上两行)然后输出 Done!
  console.log('\u001b[2F\u001b[0JDone!');
}

(async () => {
  await progressIndicator();
})();

基于ANSI转义序列来构建命令行工具

async function progressBars(count = 1) {
  const list = [];
  let i = 0;
  while (i++ < count) {
    list[i] = 0;
  }

  // 占位提供空间
  out.write(list.map(i => '').join('\n'));

  while(list.some(i => i< 100)) {
    await sleep(10);

    const unfinished = list.reduce((p, c, i) => {
      if (c < 100) p.push(i);
      return p;
    }, []);
    const randomIndex = unfinished[Math.floor(Math.random() * unfinished.length)];
    list[randomIndex] += 1;

    out.write('\u001b[1000D');
    out.write('\u001b[' + count + 'A');
    list.forEach(p => {
      const width = Math.floor(p / 4);
      console.log('[' + '#'.repeat(width) + ' '.repeat(25-width) + ']');
    });
  }

  console.log(`\u001b[1000D\u001b[${count}A\u001b[0JDone!`);
}

(async () => {
  console.log('Single progress');
  await progressBars();

  console.log();
  console.log('Multiple progress');
  await progressBars(4);
})();

基于ANSI转义序列来构建命令行工具

参考


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Language Implementation Patterns

Language Implementation Patterns

Terence Parr / Pragmatic Bookshelf / 2010-1-10 / USD 34.95

Knowing how to create domain-specific languages (DSLs) can give you a huge productivity boost. Instead of writing code in a general-purpose programming language, you can first build a custom language ......一起来看看 《Language Implementation Patterns》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

随机密码生成器
随机密码生成器

多种字符组合密码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换