% figlet "Cowsay" | ~/go/bin/cowsay -f gopher -n
______________________________________
/ ____ \
| / ___|_____ _____ __ _ _ _ |
| | | / _ \ \ /\ / / __|/ _` | | | | |
| | |__| (_) \ V V /\__ \ (_| | |_| | |
| \____\___/ \_/\_/ |___/\__,_|\__, | |
\ |___/ /
--------------------------------------
\
\
´.-::::::-.´
.:-::::::::::::::-:.
´_::: :: :::_´
.:( o :: o ):.
´::: (..) :::.
´:::::::UU:::::::´
.::::::::::::::::.
O::::::::::::::::O
-::::::::::::::::-
´::::::::::::::::´
.::::::::::::::.
oO:::::::Oo
nmyk.io/cowsay is a complete rewrite of cowsay in Go, including translations of all the cowfiles that ship with version 3.x, plus a few new ones.
It is:
go install nmyk.io/cowsay/cmd/cow{say,think}@latest
package main import ( "bytes" "text/template" log "github.com/sirupsen/logrus" "nmyk.io/cowsay" ) func main() { // use it out of the box cowsay.Cow{}.Say("mooo") // for convenience; identical to above cowsay.Cowsay("mooo") /* ______ < mooo > ------ \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || || */ // customize mood and wrap width, use cowthink style cowsay.Cow{Mood: cowsay.Wired, Width: 8}.Think("hmmmmmmmmmmmm!") /* _________ ( hmmmmmm ) ( mmmmmm! ) --------- o ^__^ o (OO)\_______ (__)\ )\/\ ||----w | || || */ // use a Mood literal instead of builtin Mood cowsay.Cow{Mood: cowsay.Mood{"xx", "U "}}.Say("im ded") /* ________ < im ded > -------- \ ^__^ \ (xx)\_______ (__)\ )\/\ U ||----w | || || */ // use one of the builtin cowfiles cow, _ := cowsay.Load("eyes") cow.Say("bufo is not in the sudoers file. This incident will be reported.") /* _______________________________________ / bufo is not in the sudoers file. This \ \ incident will be reported. / --------------------------------------- \ \ .::!!!!!!!:. .!!!!!:. .:!!!!!!!!!!!! ~~~~!!!!!!. .:!!!!!!!!!UWWW$$$ :$$NWX!!: .:!!!!!!XUWW$$$$$$$$$P $$$$$##WX!: .<!!!!UW$$$$" $$$$$$$$# $$$$$ $$$UX :!!UW$$$$$$$$$ 4$$$$$* ^$$$B $$$$\ $$$$$$$$$$$$ d$$R" "*$bd$$$$ '*$$$$$$$$$$$o+#" """" """"""" */ // or bring your own! cow, _ = cowsay.Load("/path/to/cowfile.cow") cow.Say("you did this to me") // no need for a file cowperson := ` {{.Thoughts}} {{.Thoughts}} 🧑 👋👕✌️ 👖 👠👠 ` template.Must(cowsay.Cowfiles.New("person").Parse(cowperson)) cow, _ = cowsay.Load("person") cow.Say("enjoy your tea") /* ________________ < enjoy your tea > ---------------- \ \ 🧑 👋👕✌️ 👖 👠👠 */ // cowsay all your logs log.SetFormatter(CowFormatter{}) log.Info("mooo") /* __________________________________________________________________ / {"level":"info","msg":"mooo","time":"2023-02-08T07:53:13-05:00"} \ \ / ------------------------------------------------------------------ \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || || */ } // plays nicely with your favorite Go packages func (f CowFormatter) Format(e *log.Entry) ([]byte, error) { var b bytes.Buffer ln, _ := (&log.JSONFormatter{}).Format(e) // set Width < 0 to disable word wrap for log lines cowsay.Cow{Width: -1}.Write(&b, ln, false) b.WriteByte('\n') return b.Bytes(), nil } type CowFormatter struct{}
There are many ports of cowsay to other languages that preserve the cowfiles verbatim, but without access to a Perl interpreter, much of the fun is lost in translation. For example, here is three-eyes.cow, which hacks cowsay into printing a cow with three eyes instead of two:
## ## A cow with three eyes, brought to you by dpetrou@csua.berkeley.edu ## $extra = chop($eyes); $eyes .= ($extra x 2); $the_cow = <<EOC; $thoughts ^___^ $thoughts ($eyes)\\_______ (___)\\ )\\/\\ $tongue ||----w | || || EOC
Non-Perl cowsay implementations usually omit such cowfiles. nmyk.io/cowsay includes them all, lovingly translated to Go's text/template format:
{{.Thoughts}} ^___^
{{.Thoughts}} ({{.Eyes}}{{slice .Eyes 1 2}})\_______
(___)\ )\/\
{{.Tongue}} ||----w |
|| ||
{{- /* A cow with three eyes, brought to you by dpetrou@csua.berkeley.edu */ -}}
Cowfiles are UTF-8 encoded text files that use text/template syntax for customization. Eyes and Tongue strings may be used to customize the cow's moood, and Thoughts for the trail leading up to the cow's message balloon.
Now, we Gophers have a cowfile culture we can call our own.
As a cowsay, it functions pretty similarly to the original program. Run $(go env GOPATH)/bin/cowsay for usage information.
The COWPATH environment variable, if present, will be used to search for cowfiles. It contains a colon-separated list of directories, much like PATH or MANPATH. Should always contain a file called default.cow. Files without the .cow extension are ignored. Unset COWPATH to revert to the standard set of cowfiles embedded in the nmyk.io/cowsay binary. (As long as you have nmyk.io/cowsay, you have cowfiles <3)
As compared to the original Perl script:
Pros:
Cons:
There are also some behavioral differences which may be of interest to cowsay aficionados. I believe these to be improvements.
The original script collapses single newlines ("\n") into spaces:
cowsay:
% cowsay ' 1 2 ' _______ < 1 2 > ------- \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || ||
Whereas we preserve them:
nmyk.io/cowsay:
% ~/go/bin/cowsay ' 1 2 ' ___ / \ | 1 | | 2 | \ / --- \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || ||
Additionally, the -n flag is usable when there are non-flag arguments, which is not possible in the original program.
Finally, the nmyk.io/cowsay CLI prints a usage string when invoked with no arguments, whereas the original requires -h for this behavior.
git clone https://git.nmyk.io/cowsay
Send a nice email containing a .tar.gz of your revision to cowsay@git.nmyk.io.
This module only exists because some Perl hackers had a fun idea back in 1999-2000.
Cowsay (https://linux.die.net/man/1/cowsay) was created by Tony Monroe (tony@nog.net), with suggestions from Shannon Appel (appel@CSUA.Berkeley.EDU) and contributions from Anthony Polito (aspolito@CSUA.Berkeley.EDU).