Linux & Bash · Part I · Module 13 · Text Processing on the Command Line
Selecting Columns: cut and the Field Separator
The simplest of the field tools, and the one with the clearest edges. It does one job well on strictly delimited data, and it has two limitations that are absolute rather than inconvenient — so most of the value here is in recognising which side of the line your data falls on.
Intermediate16 min readLinuxPart IFundamentalsText Processing
Part I · Module 13 · Chapter 13.2 · Selecting Columns: cut
Chapter 13.1 said a line is a row of fields. This is the tool that takes some of them.
1. Fields
$ cat results.csv
test,seed,result,cycles
alu,7,PASS,1200
fifo,9,FAIL,340
cache,3,PASS,98000
$ cut -d, -f1,3 results.csv
test,result
alu,PASS
fifo,FAIL
cache,PASS-d sets the delimiter, -f chooses the fields. The output keeps the same delimiter between them.
Field lists take ranges:
cut -d, -f2 results.csv # field 2
cut -d, -f1,3 results.csv # fields 1 and 3
cut -d, -f2-4 results.csv # fields 2 through 4
cut -d, -f3- results.csv # field 3 to the end
cut -d, -f-2 results.csv # the start through field 22. Characters Instead of Fields
$ echo "abcdefgh" | cut -c2-4
bcd-c selects by character position rather than by field, with the same range syntax. It is the right tool for genuinely fixed-width data:
cut -c1-8 timestamps.txt # the date part of an ISO timestamp
cut -c20- fixed_width.dat # everything from column 20Fixed-width is rarer than it used to be, and it is fragile — one extra character anywhere above shifts every subsequent line. Reach for -c only when the format genuinely guarantees the positions, which usually means it was produced by something old.
(-b selects by byte. For ASCII it is identical to -c; for anything else it will cut a multi-byte character in half. Use -c.)
3. The First Hard Limit: It Cannot Reorder
$ cut -d, -f3,1 results.csv
test,result
alu,PASSFields 1 and 3, in that order — not 3 then 1.
cut reads the field list as a set, not a sequence. It walks the line once, emitting the fields you asked for in the order they appear. There is no option to change this, and no way to express "field 3, then field 1".
For that you need something that can address fields individually:
$ awk -F, '{print $3 "," $1}' results.csv
result,test
PASS,alu4. The Second Hard Limit: One Separator Means One
$ printf 'alu 7 PASS\n' > spaced.txt
$ cut -d' ' -f2 spaced.txt
$ awk '{print $2}' spaced.txt
7cut gave nothing. It split on every single space, so the line is alu, empty, empty, empty, 7, empty, empty, PASS — and field 2 is the first empty one.
awk treats a run of whitespace as one separator, which is what anybody reading aligned columns assumes.
Squeezing first
If you must use cut, collapse the runs before it:
$ tr -s ' ' < spaced.txt | cut -d' ' -f2
7tr -s squeezes repeated characters into one (Chapter 13.5). It works, and it is two processes where awk is one — but it is occasionally the clearer way to say what you mean, especially when the rest of the pipeline is already cut-shaped.
5. Lines Without the Separator
$ printf 'a,b,c\nnoseparator\nd,e,f\n' | cut -d, -f2
b
noseparator
eA line with no delimiter is passed through whole. cut has nothing to split, so it emits the line unchanged.
That is sometimes convenient and sometimes a silent corruption — a header, a blank line or a footer in the middle of your data arrives in the output looking like a field value.
-s suppresses those lines instead:
$ printf 'a,b,c\nnoseparator\nd,e,f\n' | cut -d, -f2 -s
b
eUse -s whenever the data might contain lines that are not records. Tool output with a header banner or a "Total:" footer is exactly that case.
6. The Output Delimiter
GNU cut can change the separator on the way out:
cut -d, -f1,3 --output-delimiter=' | ' results.csvThis is GNU-only. BSD cut, which is what macOS ships, rejects it:
$ cut -d, -f1,3 --output-delimiter=' | ' results.csv
cut: illegal option -- -The portable approaches are tr for a single character, or awk for anything else:
cut -d, -f1,3 results.csv | tr ',' '\t' # portable, one character
awk -F, '{print $1 " | " $3}' results.csv # portable, anythingA useful corollary: cut can only keep the delimiter it was given. Converting a comma-separated file to tab-separated is a tr job, not a cut one.
7. In Practice
Three shapes that come up constantly.
Narrowing a stream mid-pipeline, which is what cut is best at:
ps -eo pid,user,etime,cmd | grep vcs | cut -c1-60Pulling one field for a loop:
cut -d, -f1 -s tests.csv | while IFS= read -r test; do
./run_sim.sh "$test"
doneNote -s, so a header line does not become a test name, and the redirect-not-pipe discipline is irrelevant here because nothing inside the loop needs to survive it. (Were it counting, Chapter 12.5 would apply.)
Reading a system file, which is cut's home ground:
cut -d: -f1 /etc/passwd | sort # every username
cut -d: -f1,6 /etc/passwd # username and home directory/etc/passwd is exactly one colon per field, no alignment, no quoting. This is the data cut was written for.
Practice
mkdir -p /tmp/cutd && cd /tmp/cutd
cat > results.csv <<'EOF'
test,seed,result,cycles
alu,7,PASS,1200
fifo,9,FAIL,340
cache,3,PASS,98000
EOF
printf 'alu 7 PASS\nfifo 9 FAIL\n' > spaced.txt
printf 'a\tb\tc\n' > tabbed.txt
printf 'a,b,c\nnoseparator\nd,e,f\n' > mixed.csv1 — Select fields and ranges.
cut -d, -f2 results.csv
cut -d, -f1,3 results.csv
cut -d, -f2-4 results.csv
cut -d, -f3- results.csv2 — Find the default delimiter.
cut -f2 tabbed.txt
cut -f2 results.csv3 — Prove it cannot reorder.
cut -d, -f3,1 results.csv | head -2
awk -F, '{print $3 "," $1}' results.csv | head -24 — Hit the whitespace limit, then work round it.
cut -d' ' -f2 spaced.txt
awk '{print $2}' spaced.txt
tr -s ' ' < spaced.txt | cut -d' ' -f25 — Watch a non-record line pass through.
cut -d, -f2 mixed.csv
cut -d, -f2 -s mixed.csv6 — Cut by character.
echo "2026-10-08T11:02:33" | cut -c1-10
echo "2026-10-08T11:02:33" | cut -c12-7 — Change the output separator portably.
cut -d, -f1,3 results.csv | tr ',' '\t'
awk -F, '{print $1 " | " $3}' results.csv8 — Read a real system file.
cut -d: -f1 /etc/passwd | head -5
cut -d: -f1,6 /etc/passwd | head -59 — Quote a metacharacter delimiter.
printf 'a|b|c\n' > piped.txt
cut -d'|' -f2 piped.txt10 — Clean up. cd /tmp && rm -rf /tmp/cutd
Check Your Understanding
1. cut -f2 data.csv returns whole lines. Why?
2. You want fields 3 and 1, in that order. What does cut -d, -f3,1 give you, and what should you use?
3. Why does cut -d' ' -f2 fail on space-aligned output?
4. What does cut do with a line containing no delimiter, and which flag changes it?
5. Give two portable ways to turn comma-separated output into tab-separated.
6. When is cut the better choice than awk?
8. Remember
-dsets the delimiter,-fchooses the fields, and ranges like-f2-4and-f3-work.- The default delimiter is a tab. A
cut -f2that returns whole lines is always this. -cselects character positions for genuinely fixed-width data; prefer it to-b.cutcannot reorder fields.-f3,1gives you 1 then 3, and there is no option for it.- That limitation is why it is fast — one pass, no memory. On a large file it is close to the speed of reading.
cutsplits on every separator, so a run of spaces makes empty fields and aligned columns give nothing.- Strictly delimited data:
cut. Aligned for a human:awk. tr -s ' 'squeezes runs first if you want to stay withcut.- A line with no delimiter passes through whole.
-ssuppresses those, which matters for headers and footers. --output-delimiteris GNU-only. Usetrorawkto change the separator portably.- Quote a delimiter that is a shell metacharacter —
-d'|',-d' ',-d$'\t'.
Selecting columns leaves the rows in the order they arrived. The next chapter reorders them — and contains the trap that puts 10 before 2 and makes a summary table quietly wrong.
Where this fits
Part of the Linux & Bash curriculum.
