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

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

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

命令行 工具 在输出时,如果是简单的进度更新,可以使用 \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转义序列来构建命令行工具

参考


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

查看所有标签

猜你喜欢:

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

Hackers

Hackers

Steven Levy / O'Reilly Media / 2010-5-30 / USD 21.99

This 25th anniversary edition of Steven Levy's classic book traces the exploits of the computer revolution's original hackers -- those brilliant and eccentric nerds from the late 1950s through the ear......一起来看看 《Hackers》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具