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

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

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

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

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

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

go get -u github.com/enodata/faker

該程序輸出了一些短語:

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)打印所有字符串:

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])
	}
}

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

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 讀取內容並加上彩虹顏色。

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() 函數中,但我們也可以在掃描每個字符時即時地將其添加到管道:

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,因為我們使用這個文件夾名稱。