/

用Go構建命令行應用程序:彩虹貓

用Go構建命令行應用程序:彩虹貓

喜歡命令行應用程序嗎?也不要錯過 cowsay 教程!

我正在尋找一些終端應用程序來尋找靈感,結果我偶然發現了 lolcat

原始碼在 https://github.com/busyloop/lolcat,並且已經有了一些Go的實現:

看起來是一個完全沒有用的東西,所以讓我們來實現它!

首先,讓我們在屏幕上打印一些值,然後我們將為它們上色,然後我們將研究如何接受用戶輸入以作為管道工作。

我使用 https://github.com/enodata/faker 生成假的輸出。

1
go get -u github.com/enodata/faker

該程序輸出了一些短語:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import (
"fmt"
"strings"

"github.com/enodata/faker"
)

func main() {
var phrases []string

for i := 1; i < 3; i++ {
phrases = append(phrases, faker.Hacker().Phrases()...)
}

fmt.Println(strings.Join(phrases[:], "; "))
}

不幸的是,這都是無聊的黑白。讓我們添加一些顏色。我們可以通過在 fmt.Printf 中插入一個逸出字符序列來實現這一點。這樣就可以以金色 #FFD700(RGB顏色碼:255,215,0)打印所有字符串:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import (
"fmt"
"strings"

"github.com/enodata/faker"
)

func main() {
var phrases []string

for i := 1; i < 3; i++ {
phrases = append(phrases, faker.Hacker().Phrases()...)
}

output := strings.Join(phrases[:], "; ")
r, g, b := 255, 215, 0 // 金色

for j := 0; j < len(output); j++ {
fmt.Printf("\033[38;2;%d;%d;%dm%c\033[0m", r, g, b, output[j])
}
}

現在我們有了一個字符串,以及讓每個字符以不同的方式上色的基礎,現在是時候加入彩虹了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main

import (
"fmt"
"math"
"strings"

"github.com/enodata/faker"
)

func rgb(i int) (int, int, int) {
var f = 0.1
return int(math.Sin(f*float64(i)+0)*127+128),
int(math.Sin(f*float64(i)+2*math.Pi/3)*127+128),
int(math.Sin(f*float64(i)+4*math.Pi/3)*127+128)
}

func main() {
var phrases []string

for i := 1; i < 3; i++ {
phrases = append(phrases, faker.Hacker().Phrases()...)
}

output := strings.Join(phrases[:], "; ")

for j := 0; j < len(output); j++ {
r, g, b := rgb(j)
fmt.Printf("\033[38;2;%d;%d;%dm%c\033[0m", r, g, b, output[j])
}
fmt.Println()
}

這就是我們想要的效果!

彩虹顏色是使用 rgb() 函數生成的,原始的Ruby代碼在 https://github.com/busyloop/lolcat/blob/master/lib/lolcat/lol.rb 中實現了這個功能。

現在讓我們編輯程序,不再提供自己的輸出,改為讓它作為管道的一部分工作。它將從 os.Stdin 讀取內容並加上彩虹顏色。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package main

import (
"bufio"
"fmt"
"io"
"math"
"os"
)

func rgb(i int) (int, int, int) {
var f = 0.1
return int(math.Sin(f*float64(i)+0)*127+128),
int(math.Sin(f*float64(i)+2*math.Pi/3)*127+128),
int(math.Sin(f*float64(i)+4*math.Pi/3)*127+128)
}

func print(output []rune) {
for j := 0; j < len(output); j++ {
r, g, b := rgb(j)
fmt.Printf("\033[38;2;%d;%d;%dm%c\033[0m", r, g, b, output[j])
}
fmt.Println()
}

func main() {
info, \_ := os.Stdin.Stat()
var output []rune

if info.Mode()&os.ModeCharDevice != 0 {
fmt.Println("該命令旨在與管道一起使用。")
fmt.Println("用法:fortune | gorainbow")
}

reader := bufio.NewReader(os.Stdin)
for {
input, \_, err := reader.ReadRune()
if err != nil && err == io.EOF {
break
}
output = append(output, input)
}

print(output)
}

它從 os.Stdin 逐個字符地讀取並將它們添加到 output 這個字符切片中。

將輸出的渲染操作提取到 print() 函數中,但我們也可以在掃描每個字符時即時地將其添加到管道:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package main

import (
"bufio"
"fmt"
"io"
"math"
"os"
)

func rgb(i int) (int, int, int) {
var f = 0.1
return int(math.Sin(f*float64(i)+0)*127+128),
int(math.Sin(f*float64(i)+2*math.Pi/3)*127+128),
int(math.Sin(f*float64(i)+4*math.Pi/3)*127+128)
}

func main() {
info, \_ := os.Stdin.Stat()

if info.Mode()&os.ModeCharDevice != 0 {
fmt.Println("該命令旨在與管道一起使用。")
fmt.Println("用法:fortune | gorainbow")
}

reader := bufio.NewReader(os.Stdin)
j := 0
for {
input, \_, err := reader.ReadRune()
if err != nil && err == io.EOF {
break
}
r, g, b := rgb(j)
fmt.Printf("\033[38;2;%d;%d;%dm%c\033[0m", r, g, b, input)
j++
}
}

這與之前的版本相同。

現在,我們可以用 fortune 和 cowsay 玩耍了。

讓我們通過運行 go buildgo install 將其變成全局命令。命令將被稱為 gololcat,因為我們使用這個文件夾名稱。

tags: [“Go”, “命令行”, “彩虹貓”]