Video Coming Soon...

Created by Zed A. Shaw Updated 2026-04-30 02:05:52

28: nl

The projects for this module are based on the GNU Coreutils. The coreutils are a collection of command line utilities frequently found in Linux. Simple versions of these is relatively easy, but feature complete versions can sometimes take months and still not be correct. A recent example of this is the Ubuntu project found out the hard way which had performance and stability problems in a Rust rewrite of GNU coreutils named uutils.

These are really good learning projects for several reasons:

  1. They teach you basic systems programming such as opening files, reading inputs, loading data, managing processes, etc.
  2. A simple first version of each project is fairly small.
  3. If you want to go farther you can attempt a full copy of the utility and learn even more.
  4. They're well documented so you know what you have to make, and you can run the existing tool to compare your results.
  5. Best of all, they don't require crazy complicated APIs or advanced programming tricks. If you know basic loops, functions, and file I/O you can probably implement most of them from scratch with a few helper API from the C++ standard library.

The Challenge

The first tool you'll implement is nl which stands for "number lines." This tool will only require using argc, and argv to get a single parameter, then everything else is something you already know.

For a first challenge I want you to read the nl documentation and attempt a version that can print any number of files with line numbers. You should be able to do this:

./builddir/nl file1.txt file2.txt file3.txt

And it will print those files in the same way that nl does it:

nl file1.txt file2.txt file3.txt

Your version of nl should also work if given no files. If that happens then you read from std::cin instead of a file.

If you are on Windows and need nl then use Windows Subsystem for Linux to start a Linux and try the command out.

If You Get Stuck

You should attempt to create your own project using what you know, but if you get stuck then peek at my solution below. Peeking at my version is not a "failure." Attempting to make your own at first will be difficult, so getting clues on how to start teaches you what to do on the next project. Keep attempting your own start and peeking when you're stuck and pretty soon you'll be able to make your own utilities by yourself.

The Code

See my first version of nlView Source file nl.cpp Only

#include <iostream>
#include <fstream>
#include <string>
#include <fmt/core.h>
#include <unistd.h>

void dump_file(std::istream& in) {
  std::string line;
  int line_count = 0;

  while(in) {
    getline(in, line);

    // nl weirdly skips empty lines by default 
    if(line == "") {
      // but still prints it?
      fmt::println("");
      continue;
    } else {
      line_count++;
      fmt::println("{:>6} {}", line_count, line);
    }
  }
}

int main(int argc, char* argv[]) {
  if(argc > 1) {
    // there's files to process
    for(int i = 1; i < argc; i++) {
      std::ifstream in_file{argv[i]};
      dump_file(in_file);
    }
  } else {
    dump_file(std::cin);
  }
}

The Discussion

As you can see there's nothing new in this code. Here's the highlights (without spoilers):

  1. I use a function to dump any file I need.
  2. I use special fmt::println() formatting to get nicer output.
  3. I use a for-loop to go through each file.
  4. A while-loop does the printing.

Otherwise, nothing special. You know all of these topics, but do your best to come up with your solution and compare it to mine.

The Practice

For these projects you're already doing all of the things in our additional practice. You are already changing the original GNU coreutils. You're already recreating them from a description. The only thing you should try to do is break your programs in as many ways as you can.

From now on, it's on you try to break your code.

Further Study

In later exercises you'll need to use something called getopt to get command line options from users. This is a simple C function that should be available on every operating system that supports the POSIX standard, although you might have to tweak your output if your version is weird. Welcome to computers. Sometimes things are different.

If you want to push this version of nl further then study this function and see if you can use it instead of my if(argc > 1) { test. If you can't figure it out then the next exercise cat uses it.

Previous Lesson Next Lesson

Register for Learn C++ the Hard Way

Register to gain access to additional videos which demonstrate each exercise. Videos are priced to cover the cost of hosting.