Why Reach for a CLI?
Most data transformation tasks can be done with a script in Python or JavaScript. But for small, focused jobs, a command-line tool often wins on three fronts. First, a one-liner is easier to read later than a script you wrote months ago. Second, installers like Homebrew or Chocolatey make CLIs far simpler to set up than managing a language runtime. Third, command-line tools are old and change slowly, so your knowledge doesn't go stale as fast as it would with a modern framework.
Miller fits that bill neatly. It's a standalone binary, so there's no virtual environment or package manager drama. It streams data rather than loading it into memory, which means you can process large files without freezing your machine. And it's actively maintained, which is always reassuring.
Getting Started
Installation is straightforward:
- Linux:
apt-get install milleror Homebrew - macOS:
brew install miller - Windows:
choco install miller
That gives you the mlr command. Verify with mlr help topics if you like, though the built-in docs may not be necessary for what follows.
Every Miller command follows the same pattern. You specify the input format, choose an operation (called a "verb"), and point at a file:
mlr [input/output file formats] [verbs] [file]
Here, --csv declares the input type, filter is the verb (dropping rows where color equals "red"), and example.csv is the target. Other common verbs include sort and cut.
Basic Operations on TV Rating Data
Peeking at Data
To illustrate the basics, we'll use a CSV of IMDb ratings for American TV dramas. The first useful verb is head, which shows only the top of a file instead of dumping everything to the console. Combining it with output formatting keeps things readable:
`mlr --csv head ./tv_ratings.csv`
That prints a table view:
titleId,seasonNumber,title,date,av_rating,share,genres
tt2879552,1,11.22.63,2016-03-10,8.489,0.51,"Drama,Mystery,Sci-Fi"
tt3148266,1,12 Monkeys,2015-02-27,8.3407,0.46,"Adventure,Drama,Mystery"
tt3148266,2,12 Monkeys,2016-05-30,8.8196,0.25,"Adventure,Drama,Mystery"
tt3148266,3,12 Monkeys,2017-05-19,9.0369,0.19,"Adventure,Drama,Mystery"
tt3148266,4,12 Monkeys,2018-06-26,9.1363,0.38,"Adventure,Drama,Mystery"
tt1837492,1,13 Reasons Why,2017-03-31,8.437,2.38,"Drama,Mystery"
tt1837492,2,13 Reasons Why,2018-05-18,7.5089,2.19,"Drama,Mystery"
tt0285331,1,24,2002-02-16,8.5641,6.67,"Action,Crime,Drama"
tt0285331,2,24,2003-02-09,8.7028,7.13,"Action,Crime,Drama"
tt0285331,3,24,2004-02-09,8.7173,5.88,"Action,Crime,Drama"
You can switch to a more human-friendly layout with --opprint, which shows aligned columns instead of raw CSV. There's also a shortcut, --c2p, that combines --csv (input) and --opprint (output).
Chaining Verbs
The real power comes from chaining operations with the then keyword rather than running separate commands.
Suppose the titleId column is noise. The cut verb removes it:
mlr --c2p cut -x -f titleId then head ./tv_ratings.csv
Now, to keep only rows for first seasons, use filter with an expression:
mlr --c2p filter '$seasonNumber == 1' then head ./tv_ratings.csv
Sorting by a column is just as direct. The command below orders by descending rating:
mlr --c2p sort -nr av_rating then head ./tv_ratings.csv
That reveals Parenthood (1990) as the highest-rated entry.
Saving Results and Changing Formats
Miller prints to the console by default. Redirect with > to write a new file:
mlr --csv sort -nr av_rating ./tv_ratings.csv > sorted.csv
To convert CSV to JSON, use the --c2j flag instead of --csv or --c2p:
mlr --c2j sort -nr av_rating ./tv_ratings.csv > sorted.json
Working Example: Top Medal Winners in Rio 2016
Now let's apply these verbs to a real problem. We have a CSV listing every athlete in the 2016 Rio Olympics, with columns for name, nationality, and medal counts per type. We want the five athletes with the most total medals.
mlr --c2p head ./athletes.csv
The file header shows the fields available:
id name nationality sex date_of_birth height weight sport gold silver bronze info
736041664 A Jesus Garcia ESP male 1969-10-17 1.72 64 athletics 0 0 0 -
532037425 A Lam Shin KOR female 1986-09-23 1.68 56 fencing 0 0 0 -
435962603 Aaron Brown CAN male 1992-05-27 1.98 79 athletics 0 0 1 -
521041435 Aaron Cook MDA male 1991-01-02 1.83 80 taekwondo 0 0 0 -
33922579 Aaron Gate NZL male 1990-11-26 1.81 71 cycling 0 0 0 -
173071782 Aaron Royle AUS male 1990-01-26 1.80 67 triathlon 0 0 0 -
266237702 Aaron Russell USA male 1993-06-04 2.05 98 volleyball 0 0 1 -
382571888 Aaron Younger AUS male 1991-09-25 1.93 100 aquatics 0 0 0 -
87689776 Aauri Lorena Bokesa ESP female 1988-12-14 1.80 62 athletics 0 0 0 -
Start by dropping columns you won't need — info, id, weight, and date_of_birth:
mlr --csv -I cut -x -f id,info,weight,date_of_birth athletes.csv
The dataset only gives separate counts for bronze, silver, and gold. To get a total, compute a new field with put:
mlr --c2p put '$medals=$bronze+$silver+$gold' then head ./athletes.csv
That adds a medals column equal to the sum of the three medal types. Then sort by that new field, descending:
mlr --c2p put '$medals=$bronze+$silver+$gold' \
then sort -nr medals \
then head ./athletes.csv
Restrict to the top five with head -n 5:
mlr --c2p put '$medals=$bronze+$silver+$gold' \
then sort -nr medals \
then head -n 5 ./athletes.csv
Finally, convert to JSON:
mlr --c2j put '$medals=$bronze+$silver+$gold' \
then sort -nr medals \
then head -n 5 ./athletes.csv > top5.json
That single command chain computes a new column, sorts, truncates, and outputs JSON. If you only wanted the top women, add a filter step:
mlr --c2p put '$medals=$bronze+$silver+$gold' then sort -nr medals then filter '$sex == "female"' then head -n 5 ./athletes.csv
The result is a clean data file:
name nationality sex height sport gold silver bronze medals
Katie Ledecky USA female 1.83 aquatics 4 1 0 5
Simone Biles USA female 1.45 gymnastics 4 0 1 5
Emma McKeon AUS female 1.80 aquatics 1 2 1 4
Katinka Hosszu HUN female 1.75 aquatics 3 1 0 4
Madeline Dirado USA female 1.76 aquatics 2 1 1 4
Miller handles the whole pipeline — cleaning, transforming, sorting, and converting — without writing a single line of application code. Next time you reach for a throwaway script, consider whether a command-line verb chain can do the job faster.



