Building unix/linux wc part I

Jonathan Zihindula
3 min readJun 10, 2024

--

I started working on coding challenges from codingchallenges.fyi. I chose to work on them using Go. The good thing about these challenges is that they are not tied to any specific programming language, and there are no predefined solutions or hints provided. Each challenge is divided into goals, and each goal has just an expectation. For this tutorial, I want to share the first goal of the first challenge: Building wc.

At first, I did not know much about wc. I just knew that it was a Linux command, and I had never used it before, so I did not know what it was for. In simple words, wc (short for word count) is a command-line tool in Unix/Linux operating systems, which is used to find out the number of newlines, words, bytes, and characters in the files specified by the file arguments. It outputs these counts to the standard output and holds a total count for all named files.Challenge Steps

Step zero

The first step is about setting up the coding environment and choosing the programming language. For me, I chose Go as the programming language.

Step one

For this step, the goal is to build a simplified version of wc that takes the command-line option -c and outputs the number of bytes in a file.

My approach for this step is:

  1. Define the flag -c to specify the file name.
  2. Check if the file name is specified and exists in the current directory.
  3. Get the file information.
  4. Print the size of the file.

This is my code base for this:

package main

import (
"flag" // Package for command-line flag parsing
"fmt" // Package for formatted I/O
"os" // Package for operating system functionality
"path/filepath" // Package for file path manipulation
)

// contains checks if a slice of strings contains a particular string
func contains(s []string, str string) bool {
for _, v := range s { // Iterate over each element in the slice
if v == str { // If the element matches the target string
return true // Return true
}
return false // Return false if no match is found
}

return false // Default return false (in case the loop doesn't find a match)
}

func main() {
// Define a command-line flag ("-c") to specify the file name
fileName := flag.String("c", "", "Specify the file")
flag.Parse() // Parse the command-line flags

// Check if the file name flag is empty
if *fileName == "" {
// If empty, panic with a message
panic("Must specify a file Name")
}

// Get the current working directory
currentDir, err := os.Getwd()
if err != nil { // Check for errors in getting the current directory
panic(err.Error()) // Panic if there's an error
}

// Initialize a slice to hold paths in the current directory
pathsInTheCurrentDir := []string{}
// Walk through the current directory
err = filepath.Walk(currentDir, func(path string, info os.FileInfo, err error) error {
if err != nil { // Check for errors while walking through the directory
fmt.Println(err.Error()) // Print the error if any
} else {
pathsInTheCurrentDir = append(pathsInTheCurrentDir, path) // Append the path to the slice
}

return err // Return the error (if any) to filepath.Walk
})

if err != nil { // Check if there was an error during filepath.Walk
panic(err.Error()) // Panic if there's an error
}

// Check if the specified file name exists in the current directory
if !contains(pathsInTheCurrentDir, *fileName) {
// Panic if the file is not found
panic("This file is not found in the current directory")
}

// Get the file info for the specified file name
fileInfo, err := os.Stat(*fileName)
if err != nil { // Check for errors in getting the file info
panic(err.Error()) // Panic if there's an error
}

// Print the size of the file and the file name
fmt.Println(fileInfo.Size(), *fileName)
}

Thanks for reading. Stay tuned for the second step, where the goal will be to support the command-line option -l that outputs the number of lines in a file.

--

--