Starting Golang: Implementing GNU Tee

In 2020? A tad bit late though

I was a bit down last night and I needed something to pass the time. I had two options: booze or coding. After a little bit of thinking I was like,

So, I poured some whisky and connected to my VPS after firing up a Google search that has been in the back of my mind for a long time: install go on centos 7. Following this simple guide I have my warm and cozy “Hello, world” program up and running. This is cool and all but, I do not want to follow a guide that shows me if or for statements to learn a new programming language. I needed a challenge to figure this language out. This challenge should cover all the basics while being a useful program. GNU Coreutils are full of this type of programs. I took a look list of coreutils.

Tee seemed like a good starting point so, here we are.

Argument Parsing and Array Slicing

To create a utility program we need to get our CLI arguments. Args[0] is the name of the program so we need to slice it out from the array.

Looping Through Arguments: For and Foreach

Tee takes an append parameter. This parameter can be passed anywhere on the argument list. So, this is a valid tee invocation:

echo 100 | tee out.txt --append out1.txt

Append, as the name states, does not truncate target files before writing to them. To check if argument list has an append, we need to loop through the list of arguments.

I do not know about the take of Go community on the use of foreach. But, I prefer them in my code since, I find them more expressive. Here is a for loop template:

for index := 0; index < count; index++ {}

Here is a foreach loop template. Just like Python’s enumerate, this lets us iterate through a tuple of index and value.

for index, element := range list {}

Opening a File, Logging Errors and Appending to List

I need to get a list of file descriptors so I can write to target files. Therefore, I need to use os.OpenFile for each file on the argument list. Opening a file can fail, so just in case, we should log the errors.

MultiWriter, Ellipsis Operator, make and CopyBuffer

MultiWriter lets you copy a buffer to multiple targets, which is exactly what tee does. Reading through its description:

Yup, thank you.

I created a function that took a io.Reader and list of io.Writers. MultiWriter accepts variadic parameters so I used … (ellipsis operator) to pass my list as a variadic argument. I needed to create a buffer for CopyBuffer to use, which is where function make came in. All in all our Tee implementation is as follows:

Full Program

Check out my repo for the full program.

Let us put this thing to use. Here are my test cases.

Tee to non-existent file names

echo 100 | ./teego out1.txt out2.txt

Tee to existing file names to test O_TRUNC

echo 100 | ./teego out1.txt out2.txt
echo 100 | ./teego out1.txt out2.txt # Test second

Tee with append to existing file names to test O_APPEND

echo 100 | ./teego --append out1.txt out2.txt

Tee with “-” in the arguments

echo 100 | ./teego out1.txt out2.txt - # - is stdout it should be ignored. We already write to stdout

Here are our results so far

Tee without an argument

echo 100 | ./teego # write to stdout only

Tee without a preceding pipe

./teego out1.txt # Scans stdin indefinitely until a CTRL+C or CTRL+D

Self-Tee

echo 100 > out.txt
cat out.txt | ./teego out.txt

Here are the rest of the results:

Thanks for reading. Any suggestion or corrections are welcome.

Cheers.

ITU CS Graduate. EGYM GmbH München