cut

Extract columns by field or character position

Updated 2026-08-14

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
20 outputs, collapsed by default

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

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

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

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

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

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