Use Go to build a command line application: lolcat

Do you like the CLI application?do not miss itCowsayThe same goes for the tutorial!

I have been looking for some inspired terminal applications, but stumbled uponLorkat.

Turned out to behttps://github.com/busyloop/lolcatAnd there are already many Go implementations:

looks likeCompletely useless stuffBuild, let's get started!

First, print some values on the screen, then color them, and then study accepting user input to act as a pipeline.

I usehttps://github.com/enodata/fakerGenerate fake output.

go get -u github.com/enodata/faker

The program outputs many phrases:

package main

import ( “fmt” “strings”

<span style="color:#e6db74">"github.com/enodata/faker"</span>

)

func main() { var phrases []string

<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">1</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">3</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
    <span style="color:#a6e22e">phrases</span> = append(<span style="color:#a6e22e">phrases</span>, <span style="color:#a6e22e">faker</span>.<span style="color:#a6e22e">Hacker</span>().<span style="color:#a6e22e">Phrases</span>()<span style="color:#f92672">...</span>)
}

<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">strings</span>.<span style="color:#a6e22e">Join</span>(<span style="color:#a6e22e">phrases</span>[:], <span style="color:#e6db74">"; "</span>))

}

Unfortunately, this is really boring black and white. Let's add some colors. We can do this by adding an escape character sequence in frontfmt.Printf. This will print all strings in gold #FFD700 and their colorRGB color codeYes (255,215,0):

package main

import ( “fmt” “strings”

<span style="color:#e6db74">"github.com/enodata/faker"</span>

)

func main() { var phrases []string

<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">1</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">3</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
    <span style="color:#a6e22e">phrases</span> = append(<span style="color:#a6e22e">phrases</span>, <span style="color:#a6e22e">faker</span>.<span style="color:#a6e22e">Hacker</span>().<span style="color:#a6e22e">Phrases</span>()<span style="color:#f92672">...</span>)
}

<span style="color:#a6e22e">output</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">strings</span>.<span style="color:#a6e22e">Join</span>(<span style="color:#a6e22e">phrases</span>[:], <span style="color:#e6db74">"; "</span>)
<span style="color:#a6e22e">r</span>, <span style="color:#a6e22e">g</span>, <span style="color:#a6e22e">b</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">255</span>, <span style="color:#ae81ff">215</span>, <span style="color:#ae81ff">0</span> <span style="color:#75715e">//gold color

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

Now that we have a string, and the basis for coloring each character in a different way, it's time to introduce the rainbow.

package main

import ( “fmt” “math” “strings”

<span style="color:#e6db74">"github.com/enodata/faker"</span>

)

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

func main() { var phrases []string

<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">1</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">3</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
    <span style="color:#a6e22e">phrases</span> = append(<span style="color:#a6e22e">phrases</span>, <span style="color:#a6e22e">faker</span>.<span style="color:#a6e22e">Hacker</span>().<span style="color:#a6e22e">Phrases</span>()<span style="color:#f92672">...</span>)
}

<span style="color:#a6e22e">output</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">strings</span>.<span style="color:#a6e22e">Join</span>(<span style="color:#a6e22e">phrases</span>[:], <span style="color:#e6db74">"; "</span>)

<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">j</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">j</span> &lt; len(<span style="color:#a6e22e">output</span>); <span style="color:#a6e22e">j</span><span style="color:#f92672">++</span> {
    <span style="color:#a6e22e">r</span>, <span style="color:#a6e22e">g</span>, <span style="color:#a6e22e">b</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">rgb</span>(<span style="color:#a6e22e">j</span>)
    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">"\033[38;2;%d;%d;%dm%c\033[0m"</span>, <span style="color:#a6e22e">r</span>, <span style="color:#a6e22e">g</span>, <span style="color:#a6e22e">b</span>, <span style="color:#a6e22e">output</span>[<span style="color:#a6e22e">j</span>])
}
<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>()

}

That's style!

Rainbow colors are usedrgb()Functions, as implemented in the original Ruby source codehttps://github.com/busyloop/lolcat/blob/master/lib/lolcat/lol.rb

Now let’s edit the program, instead of providing our own output, let’s make itWork like a pipeFor other programs. It will read from itos.StdinRainbow it.

package main

import ( “bufio” “fmt” “io” “math” “os” )

func rgb(i int) (int, int, int) { var f = 0.1 return int(math.Sin(ffloat64(i)+0)127 + 128), int(math.Sin(ffloat64(i)+2math.Pi/3)127 + 128), int(math.Sin(ffloat64(i)+4math.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

<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">info</span>.<span style="color:#a6e22e">Mode</span>()<span style="color:#f92672">&amp;</span><span style="color:#a6e22e">os</span>.<span style="color:#a6e22e">ModeCharDevice</span> <span style="color:#f92672">!=</span> <span style="color:#ae81ff">0</span> {
    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">"The command is intended to work with pipes."</span>)
    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">"Usage: fortune | gorainbow"</span>)
}

<span style="color:#a6e22e">reader</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">bufio</span>.<span style="color:#a6e22e">NewReader</span>(<span style="color:#a6e22e">os</span>.<span style="color:#a6e22e">Stdin</span>)
<span style="color:#66d9ef">for</span> {
    <span style="color:#a6e22e">input</span>, <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">reader</span>.<span style="color:#a6e22e">ReadRune</span>()
    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> <span style="color:#f92672">&amp;&amp;</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">==</span> <span style="color:#a6e22e">io</span>.<span style="color:#a6e22e">EOF</span> {
        <span style="color:#66d9ef">break</span>
    }
    <span style="color:#a6e22e">output</span> = append(<span style="color:#a6e22e">output</span>, <span style="color:#a6e22e">input</span>)
}

print(<span style="color:#a6e22e">output</span>)

}

It reads one rune at a timeos.StdinAnd add it tooutputPiece of rune.

The output rendering has been extracted into print(), but we can also pass it "in time" as each rune is scanned:

package main

import ( “bufio” “fmt” “io” “math” “os” )

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

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

<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">info</span>.<span style="color:#a6e22e">Mode</span>()<span style="color:#f92672">&amp;</span><span style="color:#a6e22e">os</span>.<span style="color:#a6e22e">ModeCharDevice</span> <span style="color:#f92672">!=</span> <span style="color:#ae81ff">0</span> {
    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">"The command is intended to work with pipes."</span>)
    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">"Usage: fortune | gorainbow"</span>)
}

<span style="color:#a6e22e">reader</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">bufio</span>.<span style="color:#a6e22e">NewReader</span>(<span style="color:#a6e22e">os</span>.<span style="color:#a6e22e">Stdin</span>)
<span style="color:#a6e22e">j</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>
<span style="color:#66d9ef">for</span> {
    <span style="color:#a6e22e">input</span>, <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">reader</span>.<span style="color:#a6e22e">ReadRune</span>()
    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> <span style="color:#f92672">&amp;&amp;</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">==</span> <span style="color:#a6e22e">io</span>.<span style="color:#a6e22e">EOF</span> {
        <span style="color:#66d9ef">break</span>
    }
    <span style="color:#a6e22e">r</span>, <span style="color:#a6e22e">g</span>, <span style="color:#a6e22e">b</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">rgb</span>(<span style="color:#a6e22e">j</span>)
    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">"\033[38;2;%d;%d;%dm%c\033[0m"</span>, <span style="color:#a6e22e">r</span>, <span style="color:#a6e22e">g</span>, <span style="color:#a6e22e">b</span>, <span style="color:#a6e22e">input</span>)
    <span style="color:#a6e22e">j</span><span style="color:#f92672">++</span>
}

}

This is the same as before.

Now we can get rich,Cowsay

Let's make it a system-wide command by running this commandgo buildwithgo install. The command will start withgololcatBecause we use it as the folder name.


More tutorials: