Linux CLI 43 🐧 cut paste and join commands
cut -d ' ' -f 1 data.txt
cut -d ' ' -f 1,3 data.txt
cut -c 1-3 example.txt
cat data.txt
cat example.txt
paste data.txt example.txt
paste -d ',' data.txt example.txt
paste data.txt example.txt > csvfile.csv
cat csvfile.csv
cat file1.txt
cat file2.txt
join file1.txt file2.txt
join -v 1 file1.txt file2.txt
join -a 1 file1.txt file2.txt
cut, paste, and join are the column-level tools of the Unix text toolbox. cut pulls fields out of each line, paste puts lines from multiple files side by side, and join combines two files on a shared key — like a miniature SQL join at the command line.
Key point: These tools work on structured text — files organized into columns or fields. cut and paste are about layout; join is about relational matching. Together they handle most of what you’d otherwise reach for a spreadsheet or database to do.
a – cut command
cut is used to extract sections from each line of input. Extraction is based on delimiters and field positions. It is mainly used for processing text files — logs, CSVs, /etc/passwd, and anything else with columns.
Syntax:
cut [options] [file...]
Common options:
| Option | Purpose |
|---|---|
-f N | Extract field N |
-f N,M | Extract fields N and M |
-f N-M | Extract a range of fields |
-d CHAR | Use CHAR as the delimiter (default: TAB) |
-c N-M | Extract characters N through M |
-b N-M | Extract bytes N through M |
--complement | Print everything except the selected fields |
-s | Suppress lines with no delimiter |
--output-delimiter=STR | Use STR between output fields |
Examples:
# Look at the data first
$ cat data.txt
alice 95 A
bob 87 B
charlie 92 A
dave 78 C
# Extract the first field (space-delimited)
$ cut -d ' ' -f 1 data.txt
alice
bob
charlie
dave
# Extract fields 1 and 3
$ cut -d ' ' -f 1,3 data.txt
alice A
bob B
charlie A
dave C
# Extract a range of fields (1 through 3)
$ cut -d ' ' -f 1-3 data.txt
alice 95 A
bob 87 B
charlie 92 A
dave 78 C
# Everything except field 2
$ cut -d ' ' -f 2 --complement data.txt
alice A
bob B
charlie A
dave C
# Extract characters 1-3 of each line
$ cat example.txt
hello world
goodbye moon
hi there
$ cut -c 1-3 example.txt
hel
goo
hi
# Extract specific characters
$ cut -c 1,3,5 example.txt
hlo
gob
h hr
# Use a different delimiter — colon
$ cut -d ':' -f 1,3 /etc/passwd | head -3
root:0
daemon:1
bin:2
# Change the output delimiter
$ cut -d ':' -f 1,3 --output-delimiter=',' /etc/passwd | head -3
root,0
daemon,1
bin,2
# Suppress lines with no delimiter
$ printf "a b\nno_delim\nc d\n" | cut -d ' ' -f 1 -s
a
c
-f vs -c — fields vs characters:
| Mode | What it does | Use when |
|---|---|---|
-f | Splits by delimiter, selects fields | Structured columns |
-c | Selects character positions | Fixed-width data |
# Fixed-width data — use -c
$ cat fixed.txt
2024-01-15T10:00:00
2024-01-16T11:30:00
$ cut -c 1-10 fixed.txt
2024-01-15
2024-01-16
Note:
cutrequires a single-character delimiter. For multi-character delimiters (like::or,), useawk -Finstead. Also,cutdoesn’t understand quotes — a comma inside a quoted CSV field will break it.
b – paste command
paste merges lines of files side by side. It reads data from each file and writes it to standard output. By default, each line from each file is separated by a TAB character.
Syntax:
paste [options] [file...]
Common options:
| Option | Purpose |
|---|---|
-d 'CHAR' | Use CHAR as the delimiter (default: TAB) |
-s | Merge files in series (one file per line) |
- | Read from stdin |
Examples:
# Look at the files
$ cat data.txt
alice
bob
charlie
$ cat example.txt
95
87
92
# Merge side by side (TAB separated)
$ paste data.txt example.txt
alice 95
bob 87
charlie 92
# Use a comma as the delimiter
$ paste -d ',' data.txt example.txt
alice,95
bob,87
charlie,92
# Save to a file
$ paste -d ',' data.txt example.txt > csvfile.csv
$ cat csvfile.csv
alice,95
bob,87
charlie,92
# Series mode — all of file1 on one line, then all of file2
$ paste -s data.txt example.txt
alice bob charlie
95 87 92
# Series mode with a delimiter
$ paste -s -d ',' data.txt
alice,bob,charlie
# Cycle through multiple delimiters
$ paste -d ',;' data.txt example.txt
alice,95
bob;87
charlie,92
# Combine more than two files
$ paste data.txt example.txt example.txt
alice 95 95
bob 87 87
charlie 92 92
# Paste from stdin with -
$ echo "extra" | paste data.txt -
alice extra
bob
charlie
paste vs cat:
| Command | Effect |
|---|---|
cat a.txt b.txt | Concatenates — file B below file A |
paste a.txt b.txt | Merges — file B beside file A |
cat a.txt b.txt paste a.txt b.txt
┌──────────┐ ┌──────────┐
│ a1 │ │ a1 b1 │
│ a2 │ │ a2 b2 │
│ a3 │ │ a3 b3 │
│ b1 │ └──────────┘
│ b2 │
│ b3 │
└──────────┘
Tip:
pasteis perfect for building CSVs from separate column files, or for adding a line number next to each line:paste <(seq 1 10) file.txt.
c – join command
join combines lines from two files on the basis of one or more fields. By default it assumes the first field is the join key. Files need to be sorted on that key for join to work correctly — just like uniq.
Syntax:
join [options] file1 file2
Common options:
| Option | Purpose |
|---|---|
-a N | Display unmatched lines from file N |
-v N | Display only unmatched lines from file N |
-t CHAR | Use CHAR as the field delimiter |
-i | Ignore case |
-1 N | Join on field N of file 1 |
-2 N | Join on field N of file 2 |
-o FORMAT | Specify output fields |
-e STR | Replace missing fields with STR |
Examples:
# Look at the files
$ cat file1.txt
1 alice
2 bob
3 charlie
4 dave
$ cat file2.txt
1 95
2 87
3 92
5 78
# Join on the first field (default)
$ join file1.txt file2.txt
1 alice 95
2 bob 87
3 charlie 92
# Note: "4 dave" and "5" have no match, so they're omitted
# Show unmatched lines from file 1
$ join -a 1 file1.txt file2.txt
1 alice 95
2 bob 87
3 charlie 92
4 dave
# "4 dave" is included with no match
# Show unmatched lines from file 2
$ join -a 2 file1.txt file2.txt
1 alice 95
2 bob 87
3 charlie 92
5 78
# Show only unmatched lines from file 1
$ join -v 1 file1.txt file2.txt
4 dave
# Show only unmatched lines from file 2
$ join -v 2 file1.txt file2.txt
5 78
# Show unmatched from BOTH files
$ join -a 1 -a 2 file1.txt file2.txt
1 alice 95
2 bob 87
3 charlie 92
4 dave
5 78
# Use a comma delimiter
$ cat f1.csv
1,alice
2,bob
3,charlie
$ cat f2.csv
1,95
2,87
3,92
$ join -t ',' f1.csv f2.csv
1,alice,95
2,bob,87
3,charlie,92
# Join on a different field
$ cat users.txt
alice 1
bob 2
charlie 3
$ cat scores.txt
95 1
87 2
92 3
$ join -1 2 -2 2 users.txt scores.txt
1 alice 95
2 bob 87
3 charlie 92
# Ignore case
$ join -i file1.txt file2.txt
# Custom output format
$ join -o 1.1,1.2,2.2 file1.txt file2.txt
1 alice 95
2 bob 87
3 charlie 92
# Replace missing fields
$ join -a 1 -e "N/A" -o 1.1,1.2,2.2 file1.txt file2.txt
1 alice 95
2 bob 87
3 charlie 92
4 dave N/A
Why sorting matters:
┌──────────────────────────────────────────────┐
│ Unsorted input │
│ │
│ file1: file2: │
│ 3 charlie 1 95 │
│ 1 alice 3 92 │
│ 2 bob 2 87 │
│ │
│ join walks both files in parallel, │
│ comparing current keys. Unsorted = chaos. │
│ │
└──────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
│ Sorted input │
│ │
│ file1: file2: │
│ 1 alice 1 95 │
│ 2 bob 2 87 │
│ 3 charlie 3 92 │
│ │
│ join matches 1-1, 2-2, 3-3 cleanly │
│ │
└──────────────────────────────────────────────┘
Always sort both files on the join key first:
sort -k1,1 file1 > f1.sortedthenjoin f1.sorted f2.sorted. The defaultsorton the whole line usually works if the key is the first field.
Complete Example Session
# ============================================
# PART 1: CUT — EXTRACT FIELDS
# ============================================
$ cat data.txt
alice 95 A
bob 87 B
charlie 92 A
dave 78 C
$ cut -d ' ' -f 1 data.txt
alice
bob
charlie
dave
$ cut -d ' ' -f 1,3 data.txt
alice A
bob B
charlie A
dave C
# ============================================
# PART 2: CUT — EXTRACT CHARACTERS
# ============================================
$ cat example.txt
hello world
goodbye moon
hi there
$ cut -c 1-3 example.txt
hel
goo
hi
# ============================================
# PART 3: CUT ON /etc/passwd
# ============================================
$ cut -d ':' -f 1,3 /etc/passwd | head -5
root:0
daemon:1
bin:2
sys:3
sync:4
# ============================================
# PART 4: PASTE — MERGE SIDE BY SIDE
# ============================================
$ cat data.txt
alice
bob
charlie
$ cat example.txt
95
87
92
$ paste data.txt example.txt
alice 95
bob 87
charlie 92
$ paste -d ',' data.txt example.txt
alice,95
bob,87
charlie,92
$ paste -d ',' data.txt example.txt > csvfile.csv
$ cat csvfile.csv
alice,95
bob,87
charlie,92
# ============================================
# PART 5: PASTE — SERIES MODE
# ============================================
$ paste -s data.txt example.txt
alice bob charlie
95 87 92
$ paste -s -d ',' data.txt
alice,bob,charlie
# ============================================
# PART 6: JOIN — BASIC
# ============================================
$ cat file1.txt
1 alice
2 bob
3 charlie
4 dave
$ cat file2.txt
1 95
2 87
3 92
5 78
$ join file1.txt file2.txt
1 alice 95
2 bob 87
3 charlie 92
# ============================================
# PART 7: JOIN — UNMATCHED LINES
# ============================================
$ join -v 1 file1.txt file2.txt
4 dave
$ join -a 1 file1.txt file2.txt
1 alice 95
2 bob 87
3 charlie 92
4 dave
$ join -a 1 -a 2 file1.txt file2.txt
1 alice 95
2 bob 87
3 charlie 92
4 dave
5 78
# ============================================
# PART 8: COMBINED PIPELINE
# ============================================
# Build a report: username, uid, shell
$ cut -d ':' -f 1,3,7 /etc/passwd | head -5
root:0:/bin/bash
daemon:1:/usr/sbin/nologin
bin:2:/usr/sbin/nologin
sys:3:/usr/sbin/nologin
sync:4:/bin/sync
# CSV from two columns
$ paste -d ',' <(cut -d: -f1 /etc/passwd) <(cut -d: -f3 /etc/passwd) | head -5
root,0
daemon,1
bin,2
sys,3
sync,4
# Join users with their shells
$ cut -d: -f1,3 /etc/passwd | sort -k1,1 > users.txt
$ cut -d: -f1,7 /etc/passwd | sort -k1,1 > shells.txt
$ join users.txt shells.txt | head -5
root 0 /bin/bash
daemon 1 /usr/sbin/nologin
bin 2 /usr/sbin/nologin
sys 3 /usr/sbin/nologin
sync 4 /bin/sync
Quick Reference
cut
| Command | Purpose |
|---|---|
cut -d ' ' -f 1 FILE | First field |
cut -d ' ' -f 1,3 FILE | Fields 1 and 3 |
cut -d ' ' -f 1-3 FILE | Range of fields |
cut -d ' ' -f 2 --complement FILE | All but field 2 |
cut -c 1-3 FILE | First 3 characters |
cut -c 1,3,5 FILE | Specific characters |
cut -d ':' -f 1 /etc/passwd | Colon-delimited field |
cut -d ':' -f 1,3 --output-delimiter=',' FILE | Custom output delimiter |
cut -d ' ' -f 1 -s FILE | Suppress lines without delimiter |
paste
| Command | Purpose |
|---|---|
paste A B | Merge side by side (TAB) |
paste -d ',' A B | Merge with comma |
paste -s A | All lines of A on one line |
paste -s -d ',' A | Series with comma |
paste A B > out.csv | Save merged output |
paste -d ',;' A B | Cycle delimiters |
paste A - | Merge A with stdin |
join
| Command | Purpose |
|---|---|
join A B | Join on first field |
join -a 1 A B | Include unmatched from A |
join -a 2 A B | Include unmatched from B |
join -a 1 -a 2 A B | Include all unmatched |
join -v 1 A B | Only unmatched from A |
join -t ',' A B | Custom delimiter |
join -i A B | Case-insensitive |
join -1 N -2 M A B | Join on different fields |
join -o 1.1,1.2,2.2 A B | Custom output format |
join -e STR -a 1 A B | Fill missing fields |
Tool Comparison
| Tool | Direction | Purpose |
|---|---|---|
cut | Vertical | Extract fields/columns |
paste | Horizontal | Merge files side by side |
join | Relational | Match on shared key |
Best Practices
✅ Do This:
# Use -d to set the delimiter explicitly
cut -d ' ' -f 1 data.txt # ✅
# Use -s to skip lines without delimiter
cut -d ' ' -f 1 -s data.txt # ✅
# Use paste to build CSVs from columns
paste -d ',' col1.txt col2.txt > out.csv # ✅
# Sort both files before join
sort -k1,1 f1 > f1.sorted
sort -k1,1 f2 > f2.sorted
join f1.sorted f2.sorted # ✅
# Use -a 1 -a 2 to include all lines
join -a 1 -a 2 f1 f2 # ✅
# Use process substitution to feed cut into paste
paste <(cut -d: -f1 /etc/passwd) <(cut -d: -f3 /etc/passwd) # ✅
# Verify with a small sample first
head file.txt | cut -d ' ' -f 1 # ✅
❌ Don’t Do This:
# Don't use cut with multi-character delimiters
cut -d '::' -f 1 file.txt # ❌ only first char used
# Don't use cut for quoted CSV fields
cut -d ',' -f 2 data.csv # ❌ breaks on "a,b"
# Don't forget to sort before join
join f1 f2 # ❌ wrong results if unsorted
# Don't confuse -a and -v
join -v 1 f1 f2 # ✅ only unmatched
join -a 1 f1 f2 # ✅ matched + unmatched
# Don't paste files with different line counts blindly
paste short.txt long.txt # ⚠️ short lines padded with tabs
# Don't use cut when awk is clearer
cut -d ' ' -f 2- file.txt # ⚠️ works, but awk is cleaner
Common Pitfalls
| Pitfall | Problem | Solution |
|---|---|---|
| Multi-char delimiter | cut uses only the first char | Use awk -F |
Unsorted join input | Wrong or missing matches | sort first |
| Quoted CSV | cut breaks on commas in quotes | Use a real CSV parser |
| TAB vs spaces | Default delimiter is TAB | Specify -d ' ' |
-a vs -v confusion | Wrong set of lines | -a = include, -v = only |
| Field numbering | Starts at 1 | -f 1 = first field |
| Empty lines | paste pads with tabs | Pre-filter blank lines |
| Range overlap | -f 1-3,2 is redundant | Use -f 1-3 |
Real-World Examples
1. Extract Usernames from /etc/passwd
$ cut -d ':' -f 1 /etc/passwd | head -5
root
daemon
bin
sys
sync
2. Extract Username and UID
$ cut -d ':' -f 1,3 /etc/passwd | head -5
root:0
daemon:1
bin:2
sys:3
sync:4
3. Build a CSV from Two Columns
$ paste -d ',' <(cut -d: -f1 /etc/passwd) <(cut -d: -f3 /etc/passwd) | head -5
root,0
daemon,1
bin,2
sys,3
sync,4
4. Add Line Numbers to a File
$ paste <(seq 1 5) file.txt
1 line one
2 line two
3 line three
4 line four
5 line five
5. Convert a Column into a Single Line
$ paste -s -d ',' data.txt
alice,bob,charlie,dave
6. Join Users with Their Scores
$ sort -k1,1 users.txt > u.sorted
$ sort -k1,1 scores.txt > s.sorted
$ join u.sorted s.sorted
alice 95
bob 87
charlie 92
7. Show Users Without Scores
$ join -v 1 u.sorted s.sorted
dave
8. Full Outer Join
$ join -a 1 -a 2 u.sorted s.sorted
alice 95
bob 87
charlie 92
dave
eve 78
9. Join with a Different Delimiter
$ join -t ',' f1.csv f2.csv
1,alice,95
2,bob,87
3,charlie,92
10. Extract Fixed-Width Columns
$ cat fixed.txt
2024-01-15T10:00:00 ERROR disk
2024-01-16T11:30:00 WARN cpu
$ cut -c 1-10,21-25 fixed.txt
2024-01-15ERROR
2024-01-16WARN
11. Merge Multiple Files Side by Side
$ paste name.txt age.txt city.txt
alice 30 Paris
bob 25 London
charlie 35 Berlin
12. Join and Format Output
$ join -o 1.1,1.2,2.2 file1.txt file2.txt
1 alice 95
2 bob 87
3 charlie 92
13. Filter Columns and Reorder
$ cut -d ':' -f 3,1 /etc/passwd | head -3
0:root
1:daemon
2:bin
14. Combine cut + sort + uniq
# Unique shells used on the system
$ cut -d ':' -f 7 /etc/passwd | sort -u
/bin/bash
/bin/sync
/usr/sbin/nologin
15. Build a Report from /etc/passwd
$ paste -d ',' \
<(cut -d: -f1 /etc/passwd) \
<(cut -d: -f3 /etc/passwd) \
<(cut -d: -f7 /etc/passwd) | head -5
root,0,/bin/bash
daemon,1,/usr/sbin/nologin
bin,2,/usr/sbin/nologin
sys,3,/usr/sbin/nologin
sync,4,/bin/sync
Visual: cut vs paste vs join
┌──────────────────────────────────────────────┐
│ cut │
│ │
│ Input: Output (field 1): │
│ a1 a2 a3 a1 │
│ b1 b2 b3 b1 │
│ c1 c2 c3 c1 │
│ │
│ Extracts columns vertically │
│ │
└──────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
│ paste │
│ │
│ File A: File B: Output: │
│ a1 b1 a1 b1 │
│ a2 b2 a2 b2 │
│ a3 b3 a3 b3 │
│ │
│ Merges files horizontally │
│ │
└──────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
│ join │
│ │
│ File A: File B: Output: │
│ 1 alice 1 95 1 alice 95 │
│ 2 bob 2 87 2 bob 87 │
│ 3 charlie 3 92 3 charlie 92 │
│ │
│ Matches rows on a shared key │
│ │
└──────────────────────────────────────────────┘
Summary
| Command | Purpose | Example |
|---|---|---|
cut -d ' ' -f 1 FILE | First field | cut -d ' ' -f 1 data.txt |
cut -d ' ' -f 1,3 FILE | Fields 1 and 3 | cut -d ' ' -f 1,3 data.txt |
cut -c 1-3 FILE | First 3 chars | cut -c 1-3 example.txt |
cut -d ':' -f 1 /etc/passwd | Colon field | cut -d: -f1 /etc/passwd |
cut -f 2 --complement FILE | All but field 2 | cut -d ' ' -f 2 --complement f |
cut -d ' ' -f 1 -s FILE | Suppress no-delim lines | cut -d ' ' -f 1 -s f |
paste A B | Merge side by side | paste data.txt example.txt |
paste -d ',' A B | Merge with comma | paste -d ',' a.txt b.txt |
paste -s A | All on one line | paste -s data.txt |
paste A B > out.csv | Save merged output | paste a b > out.csv |
join A B | Join on first field | join file1.txt file2.txt |
join -a 1 A B | Include unmatched from A | join -a 1 f1 f2 |
join -a 1 -a 2 A B | Include all unmatched | join -a 1 -a 2 f1 f2 |
join -v 1 A B | Only unmatched from A | join -v 1 f1 f2 |
join -t ',' A B | Custom delimiter | join -t ',' f1.csv f2.csv |
join -i A B | Case-insensitive | join -i f1 f2 |
join -1 N -2 M A B | Join on different fields | join -1 2 -2 2 f1 f2 |
join -o FMT A B | Custom output | join -o 1.1,1.2,2.2 f1 f2 |
join -e STR -a 1 A B | Fill missing fields | join -e N/A -a 1 f1 f2 |
Key takeaways:
cutextracts fields (-f) or characters (-c) from each line- Use
-dto set the delimiter — default is TAB, not space cutrequires single-character delimiters — for multi-char, useawk -Fpastemerges files side by side, TAB-separated by default- Use
paste -dto choose a delimiter — perfect for building CSVs - Use
paste -sto collapse a file into a single line joincombines two files on a shared key — like SQL’s JOIN- Always sort both files on the join key before joining
- Use
-a Nto include unmatched lines,-v Nfor only unmatched - Use
-tfor a custom delimiter,-ifor case-insensitive matching - Use
-oto control the output fields and-eto fill in missing values - Combine all three with process substitution for powerful pipelines
Remember: cut is vertical — it slices columns. paste is horizontal — it stitches files together. join is relational — it matches rows. All three assume structured text, so know your delimiters. cut breaks on multi-char delimiters and quoted CSV fields — reach for awk when you need more power. join needs sorted input, just like uniq. And paste is your go-to for turning separate columns into a CSV. Master these three, and you can reshape tabular text without ever opening a spreadsheet.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!