إنشاء أمر CLI باستخدام Go: cowsay

هل تعجبك تطبيقات CLI؟لا تفوتlolcatالبرنامج التعليمي كذلك!

كوزايهو أحد تلك التطبيقات التي لا يمكنك العيش بدونها.

يقوم بشكل أساسي بإنشاء صور ASCII للبقرة مع أي رسالة تقوم بتمريرها إليها ، في لقطة الشاشة أعلاه باستخدامfortuneلتوليدها. لكنها لا تقتصر على مجال الأبقار ، يمكنها طباعة طيور البطريق والمووس والعديد من الحيوانات الأخرى.

يبدو أنه تطبيق مفيد للتنقل إلى Go!

أيضًا ، أحب ترخيص اللغة الإنجليزية العادي المرفق به:

==============
cowsay License
==============

cowsay is distributed under the same licensing terms as Perl: the Artistic License or the GNU General Public License. If you don’t want to track down these licenses and read them for yourself, use the parts that I’d prefer:

(0) I wrote it and you didn’t.

(1) Give credit where credit is due if you borrow the code for some other purpose.

(2) If you have any bugfixes or suggestions, please notify me so that I may incorporate them.

(3) If you try to make money off of cowsay, you suck.

لنبدأ بتحديد المشكلة. نريد أن نقبل المدخلات من خلال أنبوب ، ونطلب من بقرتنا أن تقول ذلك.

يقرأ التكرار الأول إدخال المستخدم من الأنبوب ويطبعه مرة أخرى. ليست معقدة للغاية.

package main

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

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 | gocowsay"</span>)
	<span style="color:#66d9ef">return</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">var</span> <span style="color:#a6e22e">output</span> []<span style="color:#66d9ef">rune</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>)
}

<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">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">"%c"</span>, <span style="color:#a6e22e">output</span>[<span style="color:#a6e22e">j</span>])
}

}

نحن نفتقد البقرة ، ونحتاج أيضًا إلى لف الرسالة في بالون منسق جيدًا.

هذا هو التكرار الأول لبرنامجنا:

package main

import ( “bufio” “fmt” “io” “os” “strings” “unicode/utf8” )

// buildBalloon takes a slice of strings of max width maxwidth // prepends/appends margins on first and last line, and at start/end of each line // and returns a string with the contents of the balloon func buildBalloon(lines []string, maxwidth int) string { var borders []string count := len(lines) var ret []string

<span style="color:#a6e22e">borders</span> = []<span style="color:#66d9ef">string</span>{<span style="color:#e6db74">"/"</span>, <span style="color:#e6db74">"\\"</span>, <span style="color:#e6db74">"\\"</span>, <span style="color:#e6db74">"/"</span>, <span style="color:#e6db74">"|"</span>, <span style="color:#e6db74">"&lt;"</span>, <span style="color:#e6db74">"&gt;"</span>}

<span style="color:#a6e22e">top</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">" "</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">strings</span>.<span style="color:#a6e22e">Repeat</span>(<span style="color:#e6db74">"_"</span>, <span style="color:#a6e22e">maxwidth</span><span style="color:#f92672">+</span><span style="color:#ae81ff">2</span>)
<span style="color:#a6e22e">bottom</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">" "</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">strings</span>.<span style="color:#a6e22e">Repeat</span>(<span style="color:#e6db74">"-"</span>, <span style="color:#a6e22e">maxwidth</span><span style="color:#f92672">+</span><span style="color:#ae81ff">2</span>)

<span style="color:#a6e22e">ret</span> = append(<span style="color:#a6e22e">ret</span>, <span style="color:#a6e22e">top</span>)
<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">count</span> <span style="color:#f92672">==</span> <span style="color:#ae81ff">1</span> {
	<span style="color:#a6e22e">s</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#e6db74">"%s %s %s"</span>, <span style="color:#a6e22e">borders</span>[<span style="color:#ae81ff">5</span>], <span style="color:#a6e22e">lines</span>[<span style="color:#ae81ff">0</span>], <span style="color:#a6e22e">borders</span>[<span style="color:#ae81ff">6</span>])
	<span style="color:#a6e22e">ret</span> = append(<span style="color:#a6e22e">ret</span>, <span style="color:#a6e22e">s</span>)
} <span style="color:#66d9ef">else</span> {
	<span style="color:#a6e22e">s</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#e6db74">`%s %s %s`</span>, <span style="color:#a6e22e">borders</span>[<span style="color:#ae81ff">0</span>], <span style="color:#a6e22e">lines</span>[<span style="color:#ae81ff">0</span>], <span style="color:#a6e22e">borders</span>[<span style="color:#ae81ff">1</span>])
	<span style="color:#a6e22e">ret</span> = append(<span style="color:#a6e22e">ret</span>, <span style="color:#a6e22e">s</span>)
	<span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">1</span>
	<span style="color:#66d9ef">for</span> ; <span style="color:#a6e22e">i</span> &lt; <span style="color:#a6e22e">count</span><span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
		<span style="color:#a6e22e">s</span> = <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#e6db74">`%s %s %s`</span>, <span style="color:#a6e22e">borders</span>[<span style="color:#ae81ff">4</span>], <span style="color:#a6e22e">lines</span>[<span style="color:#a6e22e">i</span>], <span style="color:#a6e22e">borders</span>[<span style="color:#ae81ff">4</span>])
		<span style="color:#a6e22e">ret</span> = append(<span style="color:#a6e22e">ret</span>, <span style="color:#a6e22e">s</span>)
	}
	<span style="color:#a6e22e">s</span> = <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#e6db74">`%s %s %s`</span>, <span style="color:#a6e22e">borders</span>[<span style="color:#ae81ff">2</span>], <span style="color:#a6e22e">lines</span>[<span style="color:#a6e22e">i</span>], <span style="color:#a6e22e">borders</span>[<span style="color:#ae81ff">3</span>])
	<span style="color:#a6e22e">ret</span> = append(<span style="color:#a6e22e">ret</span>, <span style="color:#a6e22e">s</span>)
}

<span style="color:#a6e22e">ret</span> = append(<span style="color:#a6e22e">ret</span>, <span style="color:#a6e22e">bottom</span>)
<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">strings</span>.<span style="color:#a6e22e">Join</span>(<span style="color:#a6e22e">ret</span>, <span style="color:#e6db74">"\n"</span>)

}

// tabsToSpaces converts all tabs found in the strings // found in the lines slice to 4 spaces, to prevent misalignments in // counting the runes func tabsToSpaces(lines []string) []string { var ret []string for _, l := range lines { l = strings.Replace(l, “\t”, " ", -1) ret = append(ret, l) } return ret }

// calculatemaxwidth given a slice of strings returns the length of the // string with max length func calculateMaxWidth(lines []string) int { w := 0 for _, l := range lines { len := utf8.RuneCountInString(l) if len > w { w = len } }

<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">w</span>

}

// normalizeStringsLength takes a slice of strings and appends // to each one a number of spaces needed to have them all the same number // of runes func normalizeStringsLength(lines []string, maxwidth int) []string { var ret []string for _, l := range lines { s := l + strings.Repeat(" ", maxwidth-utf8.RuneCountInString(l)) ret = append(ret, s) } return ret }

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 | gocowsay"</span>)
	<span style="color:#66d9ef">return</span>
}

<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">lines</span> []<span style="color:#66d9ef">string</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">line</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">ReadLine</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">lines</span> = append(<span style="color:#a6e22e">lines</span>, string(<span style="color:#a6e22e">line</span>))
}

<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">cow</span> = <span style="color:#e6db74">`         \  ^__^

\ (oo)_______ (__)\ )/
||----w | || || `

<span style="color:#a6e22e">lines</span> = <span style="color:#a6e22e">tabsToSpaces</span>(<span style="color:#a6e22e">lines</span>)
<span style="color:#a6e22e">maxwidth</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">calculateMaxWidth</span>(<span style="color:#a6e22e">lines</span>)
<span style="color:#a6e22e">messages</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">normalizeStringsLength</span>(<span style="color:#a6e22e">lines</span>, <span style="color:#a6e22e">maxwidth</span>)
<span style="color:#a6e22e">balloon</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">buildBalloon</span>(<span style="color:#a6e22e">messages</span>, <span style="color:#a6e22e">maxwidth</span>)
<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">balloon</span>)
<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">cow</span>)
<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>()

}

دعنا الآن نجعل الشكل قابلاً للتكوين بإضافة aستيجوسورس

يستخدم التطبيق الأصلي ملف-fعلم لقبول شخصية مخصصة. لذلك دعونا نفعل الشيء نفسهمعالجة علامة سطر الأوامر.

أقوم بتغيير البرنامج السابق لفترة وجيزة لتقديمهprintFigure()

// printFigure given a figure name prints it.
// Currently accepts `cow` and `stegosaurus`.
func printFigure(name string) {
<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">cow</span> = <span style="color:#e6db74">`         \  ^__^

\ (oo)_______ (__)\ )/
||----w | || || `

<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">stegosaurus</span> = <span style="color:#e6db74">`         \                      .       .

\ / </span> <span style="color:#f92672">+</span> <span style="color:#e6db74">"" + . .' " </span><span style="color:#e6db74"> \ .---. &lt; &gt; &lt; &gt; .---. </span><span style="color:#e6db74"> \ | \ \ - ~ ~ - / / | </span><span style="color:#e6db74"> _____ ..-~ ~-..-~ </span><span style="color:#e6db74"> | | \~~~\\.' + ""</span> <span style="color:#f92672">+</span> <span style="color:#e6db74">./~/ --------- _/ _/ .’ O \ / / \ " (_____, </span> <span style="color:#f92672">+</span> <span style="color:#e6db74">"" + ._.' | } \/~~~/ </span><span style="color:#e6db74"> + ""</span> <span style="color:#f92672">+</span> <span style="color:#e6db74">----. / } | / __/ </span> <span style="color:#f92672">+</span> <span style="color:#e6db74">"" + -. | / | / + ""</span> <span style="color:#f92672">+</span> <span style="color:#e6db74">. ,| ~-.| /_ - ~ ^| /- _ </span> <span style="color:#f92672">+</span> <span style="color:#e6db74">"" + ..-' </span><span style="color:#e6db74"> | / | / ~-. + ""</span> <span style="color:#f92672">+</span> <span style="color:#e6db74">-. _ _ _ || |__| ~ - . _ _ _ _ _> `

<span style="color:#66d9ef">switch</span> <span style="color:#a6e22e">name</span> {
<span style="color:#66d9ef">case</span> <span style="color:#e6db74">"cow"</span>:
	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">cow</span>)
<span style="color:#66d9ef">case</span> <span style="color:#e6db74">"stegosaurus"</span>:
	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">stegosaurus</span>)
<span style="color:#66d9ef">default</span>:
	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">"Unknown figure"</span>)
}

}

ومتغيرةmain()لقبول العلم وتمريره إليهprintFigure():

func main() {
	//...

	var figure string
	flag.StringVar(&figure, "f", "cow", "the figure name. Valid values are `cow` and `stegosaurus`")
	flag.Parse()
<span style="color:#75715e">//...

printFigure(figure) fmt.Println() }

لعب

أعتقد أننا في مرحلة جيدة. أريد فقط أن أجعل هذا النظام قابلاً للاستخدام ، بدون تشغيلgo run main.go، لذلك سأكتب فقطgo buildوgo install.

يمكنني الآن قضاء اليوم معجلولكاتو gocowsay


المزيد من دروس Go: