Video Coming Soon...
31: cat
We're now going to implement many of the tools found in the GNU Coreutils collect. These are implementations of utilities found primarily on Linux, but you'll have similar commands on macOS, FreeBSD, and even Windows has something like them.
I chose the Coreutils because they're deceptively simple tools that almost anyone can create, but that have secret little tricks you only find when you really sit down to implement them. They're also well documented and you should be able to access them from any system. This makes them very good targets for doing replicas.
I also like these tools because a big use case for Go is systems programming. Systems programming is any kind of code you write that controls systems like automation tools, process tools, and network servers. By implementing your own versions of the Coreutils you'll also learn how to do things like automating your system and working with the network.
Escaping Tutorial Hell
When I talk to beginners they frequently complain about "tutorial hell." From what I can gather this is where you watch Youtube videos and read blog posts, but never actually make anything yourself.
I feel that this is mostly caused by people not learning how to go from "idea" to "working software," which is why I added the Remake It challenges throughout the course. By having you recreate my code from your own descriptions you take the first step into creating your own code from your own descriptions.
The next part in your escape from tutorial hell is to have a series of small projects you can create on your own to get better at the process. In everything you create there's always a beginning, a middle, and an end. The way you get better at this is to create many small projects that begin and end quickly. Eventually you'll learn that every large project is nothing more than many small projects chained together.
The Rules of the Game
Let's establish some rules for this little game we're going to play:
- You need to read the challenge and study the target utility to copy.
- You are then required to attempt to implement the tool with absolutely no further help from me in this exercise.
- You can search for documentation and search for clues, but you have to try to not get any help during this phase.
- Once you've attempted your own solution for 1 day you can then look at the Requirements section where I lay out the various Go modules I used to complete my first version of the project. With this information, continue your attempt.
- After one more day you can then look at the Spoilers section and only take notes on how I did it. Don't copy my code. Instead, you have to do what you did in the Remake It sections and write down notes based on my code, then use those notes to finish your version.
- After this you are allowed to completely copy my code line-by-line if you need to figure out how it works, BUT if you had to do that then you have to repeat step 5 again until you can actually Remake It.
- Finally, you then have to work on this until you get as faithful a copy of the original, or you get bored and want to move on.
The purpose of this challenge process is to get you to attempt your own projects, and then gradually give you more and more clues until you solve it. The purpose is NOT to make you feel stupid. You're still learning so it will take you a while to learn how to turn your ideas (or other people's requirements) into working code. Keep practicing and trying until you can, but if you give up because you think you "failed" then you'll never learn how to do it.
Pro-Tips
Here's some important tips to help you with this game:
- Get started fast! You want to have a
starterproject ready to go and get your project up and running without thinking about it. Don't waste tons of time finding the perfect libraries, get that thing up and running first. - Once you have it started then do some quick hacking and research "live" on your project. I find that beginners think every line they type has to be perfect, but programming is way more fun if you approach it interactively and get dirty. I think most things are more fun that way.
- After you dot his initial exploration (which we call a "spike") trash that garbage, but first take notes. Usually your first version isn't very good, but you learn a lot. Starting over with notes on how to do it usually results in a far better version, and also teaches you even more.
- Establish an end goal early. You want these projects to be quick, but if you don't have an end goal in mind you'll risk beating that code until it's glue. At the beginning decide how far you want to take it, and stop when you get there.
- Don't set impossible goals. I know, some of you out there think it'll make everyone think you're so smart if you take the
lstool and also add your own MMO video game with bitcoin to it. What's smart is picking a reasonable goal that's only a little bit beyond your skill, but not one that's too easy or too hard. - Give yourself a chance. You're just starting out, so comparing your ability to mine is unfair. Keep reminding yourself that you're a beginner and you need time to grow and learn about who you are as a programmer first.
The Challenge
The first tool you'll be required to implement is cat. You can find the documentation here:
Your goal is to create an initial cat tool, in Go, that does at least 2 things cat does. Once you have that working you should attempt to implement all of the command line options you can implement in one week.
Requirements
You'll find that theres a list of core packages you'll use in most of these tools. You'll need something to work with files, something to work with strings, and something to work with command line arguments.
You should try to use Go's Documentation Site to find the resources you need on your own. Being able to find what you need to accomplish a task is an important skill every programmer needs to learn. You can also run the pkgsite tool to get a locally hosted version of the offical docs.
The packages I used in my version of cat are:
See the list of requirements
- fmt -- https://pkg.go.dev/fmt
- flag -- https://pkg.go.dev/flag
- os -- https://pkg.go.dev/os
- log -- https://pkg.go.dev/log
- strings -- https://pkg.go.dev/strings
Spoilers
My version of cat doesn't implement everything, and most of my versions of the tool won't be complete solutions. The purpose of this little "spoiler" isn't to give you a full solution, but instead to help you get off the ground for your own solution.
The best way to use these spoilers is to do this:
- Attempt your own version of the tool.
- After your attempt, compare it to mine to see if I hve any better ideas.
- If you have no idea how to even start then take a quick look at mine and take notes.
- Attempt it based on your notes to see if you can figure it out now.
- If you're finally unable to make the tool then you should do a full copy of it.
- If you do a full copy, then follow the Remake It process: take notes on your copy, delete your copy, then recreate it from your notes.
See my first version of cat
View Source file go-coreutils/cat/main.go Onlypackage main
import (
"fmt"
"flag"
"os"
"log"
"strings"
)
func Fail(err error, format string, v ...any) {
err_format := fmt.Sprintf("ERROR: %v; %s", err, format)
log.Printf(err_format, v...)
os.Exit(1)
}
type Opts struct {
Number bool
Squeeze bool
Filenames []string
}
func parse_opts() (Opts) {
var opts Opts
flag.BoolVar(&opts.Number, "n", false, "Number all nonempty output lines, starting with 1")
flag.BoolVar(&opts.Squeeze, "s", false, "Suppress repeated adjacent blank lines")
flag.Parse()
if flag.NArg() < 1 {
log.Fatal("USAGE: cat [-n] [-s] file0 [fileN]")
os.Exit(1)
}
opts.Filenames = flag.Args()
return opts
}
func main() {
opts := parse_opts()
for _, filename := range opts.Filenames {
in_file, err := os.ReadFile(filename)
if err != nil { Fail(err, "cannot open %s:", filename) }
if(opts.Number) {
count := 1
for line := range strings.Lines(string(in_file)) {
if opts.Squeeze && len(line) <= 1 {
continue
}
fmt.Printf("%0.4d: %s", count, line)
count++
}
} else {
fmt.Print(string(in_file))
}
}
}
Testing It
If you want to push your learning further then you can try to implement an automated test for this. I actually need to learn how to test utilities like this with Go, so for now just consider this an extra challenge for later until I learn how to teach it.
Register for Learn Go the Hard Way
Register today for the course and get the all currently available videos and lessons, plus all future modules for no extra charge.