Module 3 · Text, redirection and pipes
cut, paste and tr
Slice columns out of lines, glue files side by side, and translate or delete characters: the small tools for reshaping text.
What you will learn
- Extract fields with cut -d and -f, and character ranges with -c.
- Join files line by line with paste and choose the separator.
- Change case, squeeze repeats and delete characters with tr.
A lot of the text you meet on a Unix system is tabular: /etc/passwd has colon-separated fields, CSV exports have commas, ls -l has space-aligned columns. Three small filters cover most of what you need to do with such text before handing it to sort, uniq or grep: cut picks columns, paste joins them back, and tr rewrites characters.
cut: one column, please
cut -d SEP -f N splits each line on the single character SEP and prints field number N. Fields are numbered from 1, and the list can be a range or several numbers: -f1,3, -f2-4, -f3- (from 3 to the end). The default separator is a tab, which is why cut -f1 on a space-separated file prints whole lines: there was no tab to split on. cut -c 1-3 works on character positions instead of fields, useful for fixed-width output.
~% head -3 /etc/passwd
root:x:0:0:root:/root:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/bin/false
bin:x:2:2:bin:/bin:/bin/false
~% cut -d: -f1 /etc/passwd | head -3
root
daemon
bin
~% cut -d: -f1,7 /etc/passwd | head -3
root:/bin/sh
daemon:/bin/false
bin:/bin/false
~% echo abcdef | cut -c2-4
bcd
paste: columns from files
paste a b prints line 1 of a, a tab, line 1 of b, then line 2 of each, and so on. -d chooses the separator: paste -d, a b makes a two-column CSV, paste -d' ' a b separates with a space. paste -s file goes the other way, joining all lines of one file into a single line, which is a neat way to turn a list into a b c.
tr: character by character
tr SET1 SET2 reads stdin and replaces every character in SET1 with the character at the same position in SET2. It only reads stdin, so use < or a pipe: tr 'a-z' 'A-Z' < file uppercases, and the named classes [:lower:] and [:upper:] do the same more portably. tr -d SET deletes the characters instead of translating them, tr -s SET squeezes repeated characters into one, and -c complements the set (everything *not* in it).
~% echo hello | tr 'a-z' 'A-Z'
HELLO
~% echo 'too many spaces' | tr -s ' '
too many spaces
~% echo 'ph0n3 nu3b3r' | tr -d '0-9'
phn nubr
~% echo 'a,b,c' | tr ',' '\n'
a
b
c
~% printf 'one\ntwo\n' > x; printf 'uno\ndos\n' > y
~% paste x y
one\tuno
two\tdos
~% paste -d= x y
one=uno
two=dos
tr is also how you turn one shape of text into another so the next tool can work: tr ' ' '\n' breaks a sentence into one word per line, ready for sort | uniq -c; tr -d '\r' strips the carriage returns from a file saved on Windows; tr -cs 'a-zA-Z' '\n' keeps only letters and puts each run on its own line.
| Task | Command |
|---|---|
| user names from /etc/passwd | cut -d: -f1 /etc/passwd |
| second CSV column | cut -d, -f2 file.csv |
| uppercase a file | tr 'a-z' 'A-Z' < file |
| one word per line | tr ' ' '\n' < file |
| two lists side by side | paste -d' ' a b |
Commands in this lesson
| Command | What it does |
|---|---|
cut -d: -f1 file | First :-separated field of each line. |
cut -d, -f2-4 file | Fields 2 to 4 of a CSV. |
cut -c1-5 file | Characters 1 to 5 of each line. |
paste a b | Join a and b line by line with a tab. |
paste -d, a b | Same, separated by a comma. |
tr 'a-z' 'A-Z' < file | Convert to upper case. |
tr -d '0-9' < file | Delete all digits. |
tr -s ' ' < file | Squeeze runs of spaces into one. |
Quiz
What does `cut -d: -f7 /etc/passwd` print?
- The seventh character of each line
- The login shell of each account (the seventh field)
- The first seven lines
Why does `cut -f2 data.txt` print whole lines when data.txt is separated by spaces?
- Because the default delimiter is a tab and there are none
- Because -f needs -c as well
- Because field 2 does not exist
What does `echo 'aaabbb' | tr -s 'ab'` print?
- `ab`
- `bbbaaa`
- An empty line
Which command puts names.txt and ages.txt side by side separated by a comma?
- `cat names.txt ages.txt | tr '\n' ','`
- `paste -d, names.txt ages.txt`
- `cut -d, names.txt ages.txt`
Why does `tr 'a-z' 'A-Z' file.txt` fail?
- tr only reads stdin; the file name is taken as a third set
- Ranges are not allowed in tr
- You must use -d for files
Practice
/root/lab/l27/users.csv has lines like `ana,30,madrid`. Extract only the first column (the names) into /root/lab/l27/names.txt.
Create /root/lab/l27/shout.txt with the content of /root/lab/l27/users.csv converted to upper case.
/root/lab/l27/first.txt has first names and /root/lab/l27/last.txt has surnames, one per line. Join them line by line, separated by a single space, into /root/lab/l27/full.txt (e.g. `ana garcia`).
Open this lesson in the app to do the tasks in a real Linux machine and have them checked.