cowsay

go doc -all

% 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:

browse the source code

Installation

go install nmyk.io/cowsay/cmd/cow{say,think}@latest

Usage

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{}
        

Cowfiles

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.

CLI

As a cowsay, it functions pretty similarly to the original program. Run $(go env GOPATH)/bin/cowsay for usage information.

Environment

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)

Comparison

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.

Contributing

git clone https://git.nmyk.io/cowsay

Send a nice email containing a .tar.gz of your revision to cowsay@git.nmyk.io.

Version history

Credits

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).