cut
Extract columns by field or character position
cut extracts columns from each line of input, either by field (-f, split on a
delimiter) or by character position (-c, a fixed range regardless of content). -d sets
the delimiter for -f mode; it defaults to a tab, not a space, which surprises people the first
time they try cut -f2 on space-separated text and get nothing useful back.
cut has two behaviours that catch people. First, the delimiter is exactly one character:
cut -d: -f2 on a::b sees two single-colon splits, not one double-colon split,
so it returns an empty field, not b. Second, cut always prints selected fields in their
original column order, never the order you list them in -f: cut -f3,1 still prints field
1 before field 3.
Multiple consecutive delimiters (like the padded columns in ps or ls -l output) create empty
fields the same way. Use tr -s to squeeze repeated spaces into one first, or use
awk, which handles both of these cases without the workaround.
Sample files used on this page
Every example below was run against these files. Recreate them to follow along.
users.csv a header row plus 6 records
name,age,department
Alice,34,Engineering
Bob,29,Sales
Carol,41,Engineering
Dave,25,Marketing
Erin,38,Sales
Frank,31,Engineering
users2.csv an identical copy of users.csv, for the multi-file examples
(same as users.csv)
access.log 10 requests in the standard combined log format; IP is field 1
203.0.113.5 - - [13/Aug/2026:09:12:01] "GET /index.html HTTP/1.1" 200 512
203.0.113.5 - - [13/Aug/2026:09:12:03] "GET /style.css HTTP/1.1" 200 231
198.51.100.7 - - [13/Aug/2026:09:14:22] "GET /index.html HTTP/1.1" 200 512
198.51.100.7 - - [13/Aug/2026:09:14:25] "GET /missing.html HTTP/1.1" 404 162
203.0.113.5 - - [13/Aug/2026:09:15:47] "GET /index.html HTTP/1.1" 200 512
192.0.2.44 - - [13/Aug/2026:09:16:03] "POST /login HTTP/1.1" 302 0
192.0.2.44 - - [13/Aug/2026:09:16:04] "GET /dashboard HTTP/1.1" 200 4021
198.51.100.7 - - [13/Aug/2026:09:18:51] "GET /index.html HTTP/1.1" 200 512
203.0.113.5 - - [13/Aug/2026:09:19:10] "GET /api/status HTTP/1.1" 500 89
192.0.2.44 - - [13/Aug/2026:09:20:33] "GET /dashboard HTTP/1.1" 200 4021
Selecting fields with -f
-d sets the delimiter; -f picks which field or fields to keep.
Extract one column from CSV
cut -d, -f1 users.csv
-d, treats a comma as the delimiter; -f1 keeps only the first field.
Show output
name
Alice
Bob
Carol
Dave
Erin
Frank
Extract multiple specific columns
cut -d, -f1,3 users.csv
A comma-separated list in -f keeps just those fields, each still separated by the original delimiter.
Show output
name,department
Alice,Engineering
Bob,Sales
Carol,Engineering
Dave,Marketing
Erin,Sales
Frank,Engineering
Extract a range of columns
cut -d, -f1-2 users.csv
N-M selects an inclusive range of fields.
Show output
name,age
Alice,34
Bob,29
Carol,41
Dave,25
Erin,38
Frank,31
Extract from a column to the end of the line
cut -d, -f2- users.csv
A trailing dash with no end number means 'to the last field,' how ever many there are.
Show output
age,department
34,Engineering
29,Sales
41,Engineering
25,Marketing
38,Sales
31,Engineering
Fields print in original order, not selection order
cut -d, -f3,1 users.csv
Listing field 3 before field 1 doesn't reorder the output - cut always preserves the source column order regardless of how -f lists them. Use awk if you need columns rearranged.
Show output
name,department
Alice,Engineering
Bob,Sales
Carol,Engineering
Dave,Marketing
Erin,Sales
Frank,Engineering
Selecting characters with -c
A fixed position range, independent of any delimiter.
Extract a fixed character range
echo 'Hello, World!' | cut -c1-5
-c1-5 keeps characters 1 through 5, however they're separated (or not) - no delimiter involved at all.
Show output
Hello
From a character to the end of the line
echo 'Hello, World!' | cut -c8-
Same open-range syntax as -f: a trailing dash means through the end.
Show output
World!
From the start to a character, shorthand
echo 'Hello, World!' | cut -c-5
A leading dash with no start number means 'from the first character' - equivalent to -c1-5, just shorter to type.
A single character position
echo 'Hello, World!' | cut -c1
A bare number with no range takes just that one character.
Show output
H
Multiple character ranges at once
echo 'Hello, World!' | cut -c1-3,8-12
Comma-separated ranges work the same way as with -f - pull several disjoint slices in one pass.
Show output
HelWorld
Byte positions instead of character positions
echo 'Hello, World!' | cut -b1-5
-b counts bytes rather than characters - identical to -c for plain ASCII text like this, but they diverge on multi-byte UTF-8 characters, where one character can span several bytes.
Show output
Hello
Inverting and reformatting the selection
Keep everything except a field
cut -d, -f2 --complement users.csv
--complement inverts the selection: every field except the ones named. Here, drop the age column and keep the rest.
Show output
name,department
Alice,Engineering
Bob,Sales
Carol,Engineering
Dave,Marketing
Erin,Sales
Frank,Engineering
Keep everything except several fields
cut -d, -f1,3 --complement users.csv
--complement works the same with a list or range as with a single field.
Show output
age
34
29
41
25
38
31
Invert a character range instead of a field
echo 'Hello, World!' | cut -c1-5 --complement
--complement applies to -c selections too, not just -f ones.
Show output
, World!
Change the delimiter used in the output
cut -d, -f1,3 --output-delimiter=' | ' users.csv
--output-delimiter replaces the separator in the printed result without needing the input to use it - handy for turning CSV into a more readable aligned-looking list.
Show output
name | department
Alice | Engineering
Bob | Sales
Carol | Engineering
Dave | Marketing
Erin | Sales
Frank | Engineering
Input shapes: delimiters, missing fields, multiple files
The default delimiter is tab, not space
printf 'a\tb\tc\n' | cut -f2
With no -d given, cut splits on tabs. Running cut -f2 on space-separated text with no -d ' ' just returns the whole line unchanged, since there's no tab to split on.
Show output
b
Lines without the delimiter pass through unchanged by default
printf 'a,b,c\nno-delimiter-here\nd,e,f\n' | cut -d, -f2
A line with no comma at all has nothing to cut, so cut prints it as-is rather than dropping it or erroring.
Show output
b
no-delimiter-here
e
Suppress lines that don't contain the delimiter
printf 'a,b,c\nno-delimiter-here\nd,e,f\n' | cut -d, -f2 -s
-s (--only-delimited) drops non-matching lines entirely instead of passing them through - useful when a stray line without the expected structure should be treated as noise, not data.
Show output
b
e
Requesting a field that doesn't exist returns an empty line
printf 'a,b\n' | cut -d, -f5
No error, no warning - just a blank line, since there's nothing at field 5. Worth checking for explicitly in a script rather than assuming a non-empty result.
The one-character delimiter limit, made visible
printf 'a::b::c\n' | cut -d: -f2
cut's delimiter is exactly one character. :: isn't treated as a single two-character separator - it's two single-colon splits, so field 2 is the empty string between them, not b. Field 3 is b; c is field 5.
Same limitation with padded whitespace columns
printf 'a b c\n' | cut -d' ' -f2
Multiple consecutive spaces - exactly what ps or ls -l use to align columns - create the same empty-field problem. Field 2 here is empty; the real 'b' has shifted to a much later field number.
Work around it by squeezing repeated delimiters first
printf 'a b c\n' | tr -s ' ' | cut -d' ' -f2
tr -s ' ' collapses runs of spaces down to one before cut ever sees the line, fixing the field numbering. See the tr page for -s and its other options.
Show output
b
Read from stdin via redirection instead of a filename argument
cut -d, -f1 < users.csv
Same result as passing the filename directly - cut treats redirected stdin identically to a file argument.
Show output
name
Alice
Bob
Carol
Dave
Erin
Frank
Cut from several files as one combined stream
cut -d, -f1 users.csv users2.csv
Multiple filename arguments are processed in sequence, each line cut the same way - the output doesn't distinguish which file a line came from.
Pulling a column out of real data
Extract just the IP addresses from an access log
cut -d' ' -f1 access.log
Space-delimited, field 1: the standard combined log format puts the client IP first. Feed this into sort | uniq -c for a visitor frequency count - see the uniq page.
Show output
203.0.113.5
203.0.113.5
198.51.100.7
198.51.100.7
203.0.113.5
192.0.2.44
192.0.2.44
198.51.100.7
203.0.113.5
192.0.2.44