Projectscut
A command-line program that prints chosen fields from each line of a file, or of whatever is piped into it, and matches the Unix cut tool. Building it teaches how plain text holds a table as records and fields, why splitting on a delimiter stops working once real CSV quoting shows up, and how small tools that handle one line at a time join into pipelines.
From Coding Challenges #4 ↗ by John Crickett
No sign-up. You tell the mentor your language: Java, Python or Node.js.
What you'll learn · 6 concepts
In the order the steps use them. Each one links to the best places to learn it, and you explain it in your own words once its steps are built.
Every step selects fields by position, so the program needs an exact rule for where one field ends and the next begins, starting with tab-separated lines in step 1.
Covers: one line is one record · a delimiter splits a record into fields · fields numbered from 1 · empty fields and delimiters in a row · lines with no delimiter · a carriage return stuck to the last field
POSIX: cut, cut out selected fields of each line of a file ↗ · GNU coreutils manual: cut invocation ↗ · Delimiter-separated values (Wikipedia) ↗
Step 2 runs on a CSV file whose song titles are wrapped in double quotes, and like the real cut, your program treats those quotes as ordinary characters. A title containing a comma would be split in two, and the artist would land in the wrong field.
Covers: the quoting rules in RFC 4180 · delimiters and line breaks inside quotes · a doubled quote as an escape · tracking whether you are inside quotes
RFC 4180: Common Format and MIME Type for CSV Files ↗ · IANA: text/tab-separated-values ↗ · Falsehoods Programmers Believe About CSVs (Donat Studios) ↗
Each output line depends only on its own input line, so the program can stream from step 1, and from step 4 its input may be a pipe of unknown length.
Covers: one record in memory at a time · lines split across two reads · a last line with no newline · output buffering and flushing
Why pipes sometimes get "stuck": buffering (Julia Evans) ↗ · POSIX base definitions: Definitions (see Line and Incomplete Line) ↗
Step 3 turns -f from one number into a list separated by commas or blanks, and step 1 already needs the number attached to the flag.
Covers: single numbers and ranges · commas or blanks as separators · output in input order, each field once · rejecting zero and malformed lists early · an option value attached or separate · shell quoting before the program starts
POSIX: cut, the list option-argument ↗ · POSIX base definitions: Utility Conventions (Guidelines 6 to 8) ↗ · Bash manual: Quoting ↗
Step 4 reads standard input when no file is named or when the name is a dash, and every step must write only the selected fields to standard output.
Covers: named files or standard input · the lone dash · data to standard output, messages to standard error · exit codes, and carrying on after a bad file
POSIX base definitions: Utility Conventions (Guideline 13) ↗ · stdin(3), Linux manual page ↗ · Bash manual: Exit Status ↗
Steps 2 and 3 pipe the output into head, which closes the pipe early, and step 5 counts artists with uniq on a file listed by song title rather than grouped by artist.
Covers: a pipe joins two running programs · backpressure when the reader is slow · early close and broken pipes · each tool's own contract, such as uniq and adjacent lines
pipe(7), Linux manual page ↗ · GNU coreutils manual: uniq invocation ↗ · The Art of Unix Programming: Basics of the Unix Philosophy ↗
What you'll build · 6 steps
Steps of 30 to 60 minutes, in your own editor and language. Each one has a check, so you know when it works.
Create the project, get the challenge's two sample files, one tab-separated and one comma-separated, and make sure the real cut is available to compare against.
Works whenBoth sample files show one record per line, with tabs between the fields in one and commas in the other, and the real cut runs on both.
Given -f with one field number and a file name, print that field from every line, using the tab as the delimiter.
Works whenField 2 of every line of the tab-separated sample, header included, matches what the real cut prints for the same file.
Concepts: records, fields and delimiters · line-at-a-time streaming in constant memory · A field list is a small grammar
Add -d to set the delimiter to any single character, and keep the tab as the default when -d is not given.
Works whenWith a comma as the delimiter, the first column of the CSV sample matches the real cut, quotes included, and without -d the tab-separated sample is still split on tabs. Piping the output into head ends quietly, with no broken pipe error.
Concepts: records, fields and delimiters · why splitting on the delimiter breaks real CSV · pipes and composing small tools
Let -f take a list of field numbers separated by commas or by blanks, and print the selected fields of each line joined by the delimiter.
Works whenFields 1 and 2 of both samples match the real cut, whether the list is written with a comma or with a quoted blank.
Concepts: A field list is a small grammar · records, fields and delimiters
Read standard input when no file is named, or when the file name is a single dash.
Works whenPiping the last few lines of the CSV sample into your program gives the same fields as the real cut, both with no file name and with a dash.
Concepts: the filter contract · line-at-a-time streaming in constant memory
Use your cut in a pipeline with uniq and wc, ideally your own wc from the earlier challenge, to count the artists in the CSV sample. No new feature is needed.
Works whenThe pipeline gives the same count with your cut as with the real one, and you can say what uniq needs from its input for that count to mean distinct artists.
Concepts: pipes and composing small tools