# ANSI转义序列

# [36m 是什么

曾在做云构建的时候,收到的日志中经常有[36m[2K[1G类似的字符

云构建就是讲本地构建移植到云上,需要在前端实时显示日志

2022-04-08 21:48:35⠙ [0/43] Installing vue-template-compiler@^2.6.5
2022-04-08 21:48:35⠹ [0/43] Installing vue-template-compiler@^2.6.5
2022-04-08 21:48:35⠸ [0/43] Installing vue-template-compiler@^2.6.5
2022-04-08 21:48:35
2022-04-08 21:48:35⠼ [0/43] Installing vue-template-compiler@^2.6.5
2022-04-08 21:48:35⠴ [0/43] Installing vue-template-compiler@^2.6.5
2022-04-08 21:48:35⠦ [0/43] Installing vue-template-compiler@^2.6.5
2022-04-08 21:48:36⠧ [2/43] Installing is-buffer@~1.1.6
2022-04-08 21:48:36⠇ [3/43] Installing tiny-emitter@^2.0.0
2022-04-08 21:48:36⠏ [4/43] Installing to-array@0.1.4
2022-04-08 21:48:36⠋ [4/43] Installing terraformer@~1.0.5
2022-04-08 21:48:36
2022-04-08 21:48:36⠙ [4/43] Installing eslint-plugin-vue@^4.7.1
2022-04-08 21:48:36
2022-04-08 21:48:36⠹ [4/43] Installing sortablejs@1.10.2
2022-04-08 21:48:36⠸ [4/43] Installing source-map@^0.5.7
2022-04-08 21:48:36⠼ [4/43] Installing worker-farm@^1.7.0
2022-04-08 21:48:36⠴ [4/43] Installing semver@^6.3.0
2022-04-08 21:48:36⠦ [4/43] Installing debug@=3.1.0
2022-04-08 21:48:36

感觉像是某种特殊字符,想百度下,却又无从下手,实在想不出来该搜索什么关键字

# 献出我的一血

今天正好逛到Stack Overflow,献出我的一血:

What does "[0;31m" means?

# 探索真相

根据网友的回复,所有线索都指向了 ANSI转义序列

ANSI转义序列是程序和终端交互的重要手段,ANSI转义序列是一种特殊的字符,通过这些字符,程序可以控制终端的行为、样式等(如:控制输出文本的前景色、背景色、控制光标位置等)。

以下通过多个例子来体验ANSI转义序列之美🌹

# 富文本

通过终端打印的文字默认是黑白风格,通过最基本的ANSI转义序列可以自定义文本渲染样式, 如:颜色、背景色。

print "\u001b[31mHelloWorld"

其中含义如下:

  1. 黑色:\u001b[30m
  2. 红色:\u001b[31m
  3. 绿色:\u001b[32m
  4. 黄色:\u001b[33m
  5. 蓝色:\u001b[34m
  6. 洋红色:\u001b[35m
  7. 青色:\u001b[36m
  8. 白色:\u001b[37m
  9. 重置:\u001b[0m

类似语法不仅仅在shell中支持,javaJavaScriptPython也都有一定的支持,js运行效果如下:

更多例子如下:

print "\u001b[30m A \u001b[31m B \u001b[32m C \u001b[33m D \u001b[0m"
print "\u001b[34m E \u001b[35m F \u001b[36m G \u001b[37m H \u001b[0m"

除了以上 8 中基础颜色,大多数终端还支持加亮粗体等配色或显示效果,并在原来代码后面多了;1这个标志。

  1. 亮黑色:\u001b[30;1m
  2. 鲜红色:\u001b[31;1m
  3. 亮绿色:\u001b[32;1m
  4. 亮黄色:\u001b[33;1m
  5. 亮蓝色:\u001b[34;1m
  6. 亮洋红色:\u001b[35;1m
  7. 亮青色:\u001b[36;1m
  8. 亮白:\u001b[37;1m
  9. 重置:\u001b[0m

同理,背景色代码如下:

普通背景色

  1. 背景黑色:\u001b[40m
  2. 背景红色:\u001b[41m
  3. 背景绿色:\u001b[42m
  4. 背景黄色:\u001b[43m
  5. 背景蓝色:\u001b[44m
  6. 背景洋红色:\u001b[45m
  7. 背景青色:\u001b[46m
  8. 背景白色:\u001b[47m

加亮版背景色

  1. 背景亮黑色:\u001b[40;1m
  2. 背景亮红色:\u001b[41;1m
  3. 背景亮绿色:\u001b[42;1m
  4. 背景亮黄色:\u001b[43;1m
  5. 背景亮蓝色:\u001b[44;1m
  6. 背景明亮的洋红色:\u001b[45;1m
  7. 背景亮青色:\u001b[46;1m
  8. 背景亮白色:\u001b[47;1m

测试例子一如下:

print "\u001b[40m A \u001b[41m B \u001b[42m C \u001b[43m D \u001b[0m";
print "\u001b[44m A \u001b[45m B \u001b[46m C \u001b[47m D \u001b[0m";
print "\u001b[40;1m A \u001b[41;1m B \u001b[42;1m C \u001b[43;1m D \u001b[0m";
print "\u001b[44;1m A \u001b[45;1m B \u001b[46;1m C \u001b[47;1m D \u001b[0m";

测试例子二如下:

function showcolors256() {
    local row col blockrow blockcol red green blue
    local showcolor=_showcolor256_${1:-bg}
    local white="\033[1;37m"
    local reset="\033[0m"

    echo 16 standard color codes:
    for row in {0..1}; do
        for col in {0..7}; do
            $showcolor $(( row*8 + col )) $row
        done
        echo
    done
    echo

    echo 6·6·6 RGB color codes:
    for blockrow in {0..2}; do
        for red in {0..5}; do
            for blockcol in {0..1}; do
                green=$(( blockrow*2 + blockcol ))
                for blue in {0..5}; do
                    $showcolor $(( red*36 + green*6 + blue + 16 )) $green
                done
                echo -n "  "
            done
            echo
        done
        echo
    done

    echo 24 grayscale color codes:
    for row in {0..1}; do
        for col in {0..11}; do
            $showcolor $(( row*12 + col + 232 )) $row
        done
        echo
    done
    echo
}

function _showcolor256_fg() {
    local code=$( printf %03d $1 )
    echo -ne "\033[38;5;${code}m"
    echo -nE " $code "
    echo -ne "\033[0m"
}

function _showcolor256_bg() {
    if (( $2 % 2 == 0 )); then
        echo -ne "\033[1;37m"
    else
        echo -ne "\033[0;30m"
    fi
    local code=$( printf %03d $1 )
    echo -ne "\033[48;5;${code}m"
    echo -nE " $code "
    echo -ne "\033[0m"
}

showcolors256

其他修饰效果

  1. 粗体 \u001b[1m
  2. 下划线 \u001b[4m
  3. 反转 \u001b[7m

# 光标导航

相比文本修饰转义字符,光标导航相关转义字符更为复杂, 它允许程序控制光标的位置、删除部分内容等等。

# 移动光标

基本操作

  1. 上:\u001b[{n}A
  2. 下:\u001b[{n}B
  3. 左:\u001b[{n}D
  4. 右:\u001b[{n}C

进阶操作

  1. 后N行 :\u001b[{n}E将光标向下移动到n行首
  2. 前N行 :\u001b[{n}F将光标向下移动到n行首
  3. 设置列 :\u001b[{n}G将光标移动到列n
  4. 设置位置 :\u001b[{n};{m}H将光标移动到n行列m
  5. 保存位置:\u001b[{s}保存当前光标位置
  6. 保存位置:\u001b[{u}将光标恢复到上次保存的位置

利用光标的上下左右移动,我们可以做一个加载进度效果,代码如下:

# 初级进度条
#!/bin/bash

for((i=1;i<100;i++))
do
  printf "\u001b[1000D 进度:${i}";
  sleep 1;
done

运行效果如下:

# 高级进度条
#!/bin/bash
i=0
str=""
arry=("\\" "|" "/" "-")
while [ $i -le 100 ]
do
    let index=i%4
    printf "\u001b[1000D[%-100s] %d %c" "$str" "$i" "${arry[$index]}"
    sleep 0.1
    let i=i+1
    str+="#"
done
echo ""

运行效果如下:

通过不断将光标左移 1000 个字符从而实现后打印的字符覆盖前打印的字符。

# 删除内容
  1. 清屏: \u001b[{n}J

n=0从光标清除直到屏幕结束

n=1从光标清除到屏幕开头

n=2清除整个屏幕

  1. 清行: \u001b[{n}K

n=0从光标清除到行尾

n=1从光标清除到行首

n=2清除整行

# 不忘初心

学习完毕,不忘初心,让我们回到最初的问题上,日志中的这些特殊字符到底什么含义呢?

2022-04-08 21:48:36⠸ [4/43] Installing source-map@^0.5.7
2022-04-08 21:48:36⠼ [4/43] Installing worker-farm@^1.7.0

# 答案

[2K该字符前其实有一个不可见字符\u001b(Esc字符),根据上文中的学习资料, \u001b[2K的含义就是清除整行

[1G同理,将光标移动到第一列

[36m同理,设置后面的文本样式为青色

如果前端在显示日志的时候能支持这些控制指令的话,那最终效果将和在终端安装依赖类似:安装进度不断在同一行刷新刷新刷新...

the end
最后更新: Wed, 09 Aug 2023 14:27:36 GMT

0 评论

加载中...
访问量:-