Columns and Characters: cut and tr

cut keeps the columns you name with -f, splitting each line at the delimiter you give with -d (or keeps character positions with -c), and tr changes characters one for one, such as capitals into lowercase, or deletes them with -d.

Apprentice · Unix · Linux · Updated

After this lesson you can

  • Take columns out of a CSV file with cut -d and -f, or characters with cut -c
  • Change characters with tr, such as capitals into lowercase, or delete them with tr -d

Key takeaways

  • cut -d, -f4 splits each line at its commas and keeps the fourth column. -f3,5 keeps two columns, and -f2-4 keeps the second to the fourth.
  • Without -d, cut splits at tabs, so the lines of a CSV come out whole. Always give the delimiter.
  • cut -c1-7 keeps the first seven characters of each line, such as the year and month of a date.
  • tr 'A-Z' 'a-z' changes each capital into its lowercase letter, and tr -d deletes the characters you list. tr reads only from a pipe or <.

Bea wants to email her customers about the holiday menu. Every email address is in orders.csv, but buried among dates, cakes and totals, and each regular customer is in there dozens of times. Copying the addresses out by hand would take an evening, and a spreadsheet can delete columns but won't fix the capital letters some customers typed. Two small commands do it in a line: cut takes a column out of every line, and tr fixes characters. You'll reach for them whenever you need one column from a sign-up sheet, a report or a log.

The examples use the bakery folder from Pipes: | (its setup lines make it). Go into it with cd ~/terminal-practice/bakery.

cut: one column

cut keeps the columns you ask for. Tell it two things: what separates one column from the next, with -d (for delimiter, the character between columns, here a comma), and which column you want, with -f (for field, cut's word for a column), counting from 1. Read -d, aloud as "delimiter comma". The email is the fourth column:

cut -d, -f4 orders.csv | head -n 4
email
zoe.hart@example.com
luis.ortega@example.com
Rosa.Silva@example.com

List several fields with commas between them, or a range with a dash: -f2-4 keeps the second to the fourth.

cut -d, -f3,5 orders.csv | head -n 4
customer,city
Zoe Hart,Portland
Luis Ortega,Bend
Rosa Silva,Corvallis

The header line comes along, because it's a line like any other; add tail -n +2 when you don't want it.

Forget -d, and nothing is cut

Leave out -d, and cut splits at tabs, its default. A CSV file has no tabs, so every line is one long column, and cut prints it whole:

cut -f4 orders.csv | head -n 2
order,date,customer,email,city,item,qty,total
1001,2025-01-01,Zoe Hart,zoe.hart@example.com,Portland,sourdough loaf,3,24.00

There's no error, only lines that didn't change. One more limit: cut doesn't understand quotes, so a field with a comma inside it, such as "Lopez, Maya", splits in the wrong place. Bea's export has none.

cut -c: by character

-c (for characters) counts character positions instead of fields. A date such as 2025-03-14 starts with its year and month, the first seven characters, so this counts the orders in each month with the pattern from Count, Sort, Remove Duplicates: wc, sort and uniq:

cut -d, -f2 orders.csv | tail -n +2 | cut -c1-7 | sort | uniq -c | head -n 3
     45 2025-01
     40 2025-02
     44 2025-03

tr: change characters

Back to the mailing list. sort -u keeps each address once, and wc -l counts them:

cut -d, -f4 orders.csv | tail -n +2 | sort -u | wc -l
40

Forty addresses, but Bea has only 20 customers. Some customers typed their address with capitals now and then:

grep -i "rosa.silva" orders.csv | cut -d, -f4 | head -n 3
Rosa.Silva@example.com
rosa.silva@example.com
rosa.silva@example.com

Email ignores capitals, but sort doesn't, so Rosa counts twice. tr (translate) swaps characters one for one: each character in the first set becomes the character in the same place in the second. 'A-Z' (read "A to Z") is every capital, and 'a-z' every lowercase letter:

echo "Rosa.Silva@example.com" | tr 'A-Z' 'a-z'
rosa.silva@example.com

Put tr in the pipeline before sort -u, and save the list:

cut -d, -f4 orders.csv | tail -n +2 | tr 'A-Z' 'a-z' | sort -u > mailing-list.txt
wc -l mailing-list.txt
head -n 3 mailing-list.txt
20 mailing-list.txt
aiko.tanaka@example.com
ben.carter@example.com
caleb.wells@example.com

Twenty customers, twenty addresses. The same trick turns a CSV into tab-separated columns: tr ',' '\t'.

tr -d deletes, and tr reads only its input

Some exports put quotes around every field, and cut keeps them. tr -d (for delete) removes every character you list. Careful: -d means delimiter to cut and delete to tr. Each command chooses what its letters mean.

echo '"Zoe Hart","Portland","sourdough loaf"' | tr -d '"'
Zoe Hart,Portland,sourdough loaf

The slip people make with tr is giving it a file name. It never takes one: it reads only standard input.

tr 'A-Z' 'a-z' orders.csv
tr: extra operand 'orders.csv'
Try 'tr --help' for more information.

Send the file in through a pipe, or with < from Three Streams: Output, Errors and Input:

tr 'A-Z' 'a-z' < orders.csv | head -n 2
order,date,customer,email,city,item,qty,total
1001,2025-01-01,zoe hart,zoe.hart@example.com,portland,sourdough loaf,3,24.00

Check yourself

2 questions to try on the lesson page.

So what

With cut and tr, any column of any export is yours to count, clean and save: an email list, the months that sell best, the cities to deliver to.

Key terms

Cheat sheets

Related