Post

Implementing the "head" Unix command

Have I mentioned how much I love John Crickett’s coding challenges? In this post, I cover the completion of my 2nd coding challenge, an implementation (in Python) of the unix/Unix command-line tool head.

About

py-head is a minimal implementation of the Unix-style command-line tool named head. It is a command-line tool that displays the first n lines or c bytes of a file, where the user can provide the value for n and c. If no file or value for n or c is provided, then it displays the first 10 lines from the standard input.

Similar to my py-wc project, I decided to code this up in Python since that’s the language I’m most familiar with. This allowed me to focus more on the workflow of head and less on the syntax of the language I was working with.

Instructions

To use this as a command-line tool, I recommend adding the finished script to PATH / system variables. For Windows, create a folder named Aliases in your C drive: C:/Aliases, and then add this folder to PATH. Next, create a batch file that will execute when you call the specified alias. For example, on my machine, I have a batch file named head.bat located at C:/Aliases, that contains the following script:

1
2
3
@echo off
echo.
python C:\...\GitHub\py-head\main.py %*

So now, when I type head in the command prompt, this batch file will execute, which in turn, runs the py-head Python script.

Examples

py-head allows you to execute typical Unix-style head commands. If no flags are included with the filename, it prints the entire file contents:

C:\> head hello.txt
hello, world!
C:\> head small.txt
The Project Gutenberg eBook of The Art of War

This ebook is for the use of anyone anywhere in the United States and
most other parts of the world at no cost and with almost no restrictions
whatsoever. You may copy it, give it away or re-use it under the terms
of the Project Gutenberg License included with this ebook or online
at www.gutenberg.org. If you are not located in the United States,
you will have to check the laws of the country where you are located
before using this eBook.

If no file is included, it displays the first 10 lines from standard input:

C:\> head
Some text that I entered
Some text that I entered
line 2
line 2
line 3
line 3
this is line 4
this is line 4
now this is line 5
now this is line 5
hello
hello
world
world
this is line 8
this is line 8
line 9
line 9
10!
10!

We can also specify and display only the first n lines (note that you can list the flag in any order):

C:\> head small.txt -n3
The Project Gutenberg eBook of The Art of War

This ebook is for the use of anyone anywhere in the United States and
C:\> head small.txt -n 3
The Project Gutenberg eBook of The Art of War

This ebook is for the use of anyone anywhere in the United States and
C:\> head -n3 small.txt 
The Project Gutenberg eBook of The Art of War

This ebook is for the use of anyone anywhere in the United States and
C:\> head -n 3 small.txt 
The Project Gutenberg eBook of The Art of War

This ebook is for the use of anyone anywhere in the United States and

And we can also specify and display only the first c bytes:

C:\> head large.txt -c243
The Project Gutenberg eBook of The Art of War

This ebook is for the use of anyone anywhere in the United States and
most other parts of the world at no cost and with almost no restrictions
whatsoever. You may copy it, give it away or
C:\> head large.txt -c 42 
The Project Gutenberg eBook of The Art of

We can also pass in multiple files

C:\> head hello.txt small.txt hello.txt 
==> hello.txt <==
hello, world!

==> small.txt <==
The Project Gutenberg eBook of The Art of War

This ebook is for the use of anyone anywhere in the United States and
most other parts of the world at no cost and with almost no restrictions
whatsoever. You may copy it, give it away or re-use it under the terms
of the Project Gutenberg License included with this ebook or online
at www.gutenberg.org. If you are not located in the United States,
you will have to check the laws of the country where you are located
before using this eBook.

==> hello.txt <==
hello, world!
C:\> head hello.txt hello.txt hello.txt hello.txt
==> hello.txt <==
hello, world!

==> hello.txt <==
hello, world!

==> hello.txt <==
hello, world!

==> hello.txt <==
hello, world!

And finally, we can pass in multiple files, along with the n or c flag:

C:\> head hello.txt hello.txt hello.txt hello.txt -c9
==> hello.txt <==
hello, wo

==> hello.txt <==
hello, wo

==> hello.txt <==
hello, wo

==> hello.txt <==
hello, wo
C:\> head large.txt small.txt hello.txt -n 7
==> large.txt <==
The Project Gutenberg eBook of The Art of War

This ebook is for the use of anyone anywhere in the United States and
most other parts of the world at no cost and with almost no restrictions
whatsoever. You may copy it, give it away or re-use it under the terms
of the Project Gutenberg License included with this ebook or online
at www.gutenberg.org. If you are not located in the United States,

==> small.txt <==
The Project Gutenberg eBook of The Art of War

This ebook is for the use of anyone anywhere in the United States and
most other parts of the world at no cost and with almost no restrictions
whatsoever. You may copy it, give it away or re-use it under the terms
of the Project Gutenberg License included with this ebook or online
at www.gutenberg.org. If you are not located in the United States,

==> hello.txt <==
hello, world!

Libraries

Just like the py-wc project, everything I needed for py-head was included in Python’s Standard Library. Some basic I/O with Python’s built-in open function, command-line parsing with the argparse library, and some directory navigation with os.

If you’ve never used argparse, I highly recommend it for handling parsing of command-line inputs for programs, here’s an example from my repo for this project:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
if __name__ == '__main__':
    # Create an ArgumentParser object
    parser = argparse.ArgumentParser(description='Process text file(s).')

    # Add arguments for input file(s)
    parser.add_argument(
        'input_files', 
        nargs='*',  # 0 or more input files
        type=str,
        default=[],
        help='Path to the input file(s). Pass no options with input file to print out the contents of the entire file. Pass no file to read from user input.'
    )

    # Create a mutually exclusive group for -c and -n flags
    group = parser.add_mutually_exclusive_group()

    # Add flags for different options to the mutually exclusive group created above
    group.add_argument('-c', '--bytes', type=int, default=None, help='Print bytes')
    group.add_argument('-n', '--lines', type=int, default=None, help='Print lines')

    # Parse the command-line arguments
    args = parser.parse_args()

    # more code below...

The full code for this project can be found at my Github here.

Acknowledgements

Thanks to John Crickett for the idea from his site, Coding Challenges!

Text samples were downloaded from this site.

If you happen to peruse my code and notice any bugs or opportunities for optimizations, please let me know!

This post is licensed under CC BY 4.0 by the author.