cut and tr — The Quiet Tools That Clean Up Everyone Else’s Mess
grep finds things. cut and tr shape what you do with them.
Most practitioners pick these up by accident — copy a one-liner from somewhere, it works, move on. But understanding what cut and tr actually do changes how you build pipelines. You stop reaching for awk when cut is enough. You stop writing substitution logic by hand when tr handles it in seconds.
This article covers both tools from the ground up — what they do, every flag worth knowing, and how they show up in real security workflows.
Two Tools, One Job Between Them
Before diving in, it helps to understand what each tool is actually for:
cut — extracts specific columns or fields from structured text. If data has a consistent separator (colon, comma, tab, space), cut pulls out exactly the piece you want.
tr — translates, deletes, or squeezes individual characters. It does not understand fields or structure — it works one character at a time across the entire input.
They solve different problems. They often work together in the same pipeline.
Part One — cut
What cut Does
cut reads input line by line and extracts a portion of each line based on rules you give it. There are two ways to tell it what to extract: by character position, or by field (delimited column).
The basic structure:
bash
cut [flags] filename
Or receiving input from a pipe:
bash
some_command | cut [flags]
The Core Flags
-d — Define the Delimiter
A delimiter is the character that separates fields in your data. A colon : in /etc/passwd. A comma , in a CSV. A tab in TSV output.
-d tells cut what character to split on.
bash
cut -d':' filename
In field mode (-f), cut defaults to tab as the delimiter when -d is not specified. In character mode (-c), no delimiter applies — position is everything. Since most structured security output uses colons, commas, or spaces rather than tabs, you will almost always set -d explicitly when working with -f.
-f — Choose Which Field to Extract
Once you have defined the delimiter, -f tells cut which field (column) you want.
Fields are numbered starting at 1, left to right.
bash
cut -d':' -f1 /etc/passwd
/etc/passwd uses : as the delimiter. Field 1 is the username. This command extracts only usernames from the file.
Input:
root:x:0:0:root:/root:/bin/bash
admin:x:1000:1000::/home/admin:/bin/bash
guest:x:1001:1001::/home/guest:/bin/sh
Output:
root
admin
guest
Seven fields in each line. You asked for field 1. That is all you get.
Extracting Multiple Fields
Pass a comma-separated list to -f:
bash
cut -d':' -f1,3 /etc/passwd
Extracts field 1 (username) and field 3 (UID).
Output:
root:0
admin:1000
guest:1001
cut prints the input delimiter between output fields by default. To change what appears between them, use --output-delimiter covered below.
Extracting a Range of Fields
Use a hyphen for a range:
bash
cut -d':' -f1-3 /etc/passwd
Extracts fields 1, 2, and 3.
bash
cut -d':' -f3- /etc/passwd
Extracts from field 3 to the end of the line. No upper bound needed — the - means “everything from here onward.”
bash
cut -d':' -f-3 /etc/passwd
Extracts from the start of the line up to and including field 3.
-c — Extract by Character Position
Instead of fields, extract specific character positions. Useful when data does not have a consistent delimiter but has consistent column widths.
bash
cut -c1-10 filename
Extracts the first 10 characters of every line.
bash
cut -c5 filename
Extracts only the 5th character of every line.
bash
cut -c1,5,10 filename
Extracts characters at positions 1, 5, and 10.
When to use it: Fixed-width log formats, banner grabbing output, anything where position matters more than delimiter.
--output-delimiter — Change the Separator in Output
By default cut uses the same delimiter in output as you specified with -d. You can change what appears between extracted fields.
bash
cut -d':' -f1,3 --output-delimiter=' -> ' /etc/passwd
Output:
root -> 0
admin -> 1000
guest -> 1001
Useful for reformatting data before passing it to another tool or writing to a report.
cut in Security Workflows
Extracting Usernames from /etc/passwd
bash
cut -d':' -f1 /etc/passwd
Field 1 is the username. Clean list, nothing else.
Extracting Login Shells
bash
cut -d':' -f7 /etc/passwd
Field 7 is the login shell. Shows you which users have /bin/bash, which have /bin/sh, which have /sbin/nologin — useful for identifying interactive accounts during post-exploitation.
Pulling Open Ports from Nmap Output
After filtering with grep:
bash
grep "open" nmap.txt | cut -d'/' -f1
Each open port line looks like 80/tcp open http. The / is the delimiter. Field 1 is the port number. You get a clean list of port numbers, nothing else.
Parsing CSV Output from Tools
Many tools export CSV. cut handles it directly:
bash
cut -d',' -f2,4 results.csv
Extracts columns 2 and 4 from a comma-separated file.
Cleaning Up gobuster Output
gobuster output often looks like:
/admin (Status: 200) [Size: 1234]
/login (Status: 200) [Size: 987]
/backup (Status: 403) [Size: 456]
To extract just the paths:
bash
grep "(Status: 200)" gobuster.txt | cut -d' ' -f1
grep filters to 200 responses. cut splits on space and takes field 1 — the path.
Extracting IPs from a Colon-Separated List
If you have output formatted as hostname:ip:port:
bash
cut -d':' -f2 hosts.txt
Pulls only the IP column.
What cut Cannot Do
cut is deliberately limited. It extracts fields by position — it does not filter, it does not do logic, and it does not collapse repeated spaces into a single separator.
If your data uses multiple spaces between columns, cut treats each space as its own field. Column 3 becomes field 6 or field 9 depending on spacing, and your output breaks. That is the real reason awk handles space-separated data more gracefully — it splits on runs of whitespace by default, not on individual characters.
cut is for clean, consistently delimited data. When the structure is irregular, reach for a different tool.
Part Two — tr
What tr Does
tr stands for translate. It reads from standard input and writes to standard output, replacing or deleting individual characters according to rules you give it.
tr reads from standard input and writes to standard output. It does not accept a filename as an argument — you feed it data via a pipe or input redirection.
bash
echo "hello" | tr 'a-z' 'A-Z'
Output:
HELLO
tr took every lowercase letter and replaced it with its uppercase equivalent. One character at a time, across the entire input.
The Core Flags
No Flag — Basic Character Translation
The fundamental tr call takes two sets of characters. Every character in set1 gets replaced by the corresponding character in set2.
bash
tr 'set1' 'set2'
bash
echo "hello world" | tr 'aeiou' '*****'
Output:
h*ll* w*rld
Every vowel replaced with *. Position matters — the first character in set1 maps to the first character in set2.
bash
echo "hello" | tr 'a-z' 'A-Z'
Lowercase to uppercase. a maps to A, b maps to B, and so on through the full range.
-d — Delete Characters
-d deletes every character in the specified set from the input. No replacement — they disappear.
bash
echo "h3ll0 w0rld" | tr -d '0-9'
Output:
hll wrld
Every digit deleted.
bash
echo "192.168.1.1" | tr -d '.'
Output:
19216811
Dots removed. Not always useful on its own, but useful when you need to strip specific characters from a stream before passing it somewhere else.
Stripping non-alphanumeric characters from extracted data, cleaning up encoded output, removing punctuation from a wordlist.
-s — Squeeze Repeated Characters
-s replaces consecutive repeated characters with a single instance of that character.
bash
echo "hellooooo world" | tr -s 'o '
Output:
hello world
Multiple o characters squeezed to one. Multiple spaces squeezed to one.
Normalizing inconsistent whitespace before parsing. Some tools pad columns with multiple spaces. tr -s ' ' reduces that to single spaces so cut can split fields cleanly.
bash
tr -s ' ' < messy_output.txt | cut -d' ' -f3
Squeeze multiple spaces first, then cut on single space. This is a common two-step pattern.
-c — Complement
-c is not a standalone operation — it modifies how -d or -s behaves. It inverts the character set, so whatever you specify, tr operates on everything else instead.
Used with -d:
bash
echo "hello world 123" | tr -cd 'a-zA-Z'
Breaking this down:
-c— complement: invert the seta-zA-Zto mean everything that is NOT a letter-d— delete those characters
Everything that is not a letter gets deleted. Numbers, spaces, punctuation — gone.
Output:
helloworld
Security use: Stripping everything except letters from a string. Swap a-zA-Z for a-zA-Z0-9 to keep alphanumeric characters and strip everything else — useful when extracting clean tokens or identifiers from noisy output.
Combining -c and -s
bash
tr -cs 'a-zA-Z' 'n' < file.txt
-c— complement: target everything that is NOT a letter-s— squeeze: collapse runs of those characters into one before replacing- The replacement character is
n(newline)
Each run of non-letter characters — spaces, punctuation, digits, multiple spaces in a row — gets squeezed into a single newline. The result is one word per line, with all punctuation and numbers stripped out. A quick way to extract a word list from any block of text.
Character Classes in tr
Instead of writing out character ranges manually, tr supports POSIX character classes:
| Class | What It Matches |
|—-|—-|
| [:alpha:] | All letters (upper and lower) |
| [:digit:] | All digits 0-9 |
| [:alnum:] | Letters and digits |
| [:upper:] | Uppercase letters |
| [:lower:] | Lowercase letters |
| [:space:] | Whitespace (space, tab, newline) |
| [:punct:] | Punctuation characters |
bash
echo "Hello World 123" | tr '[:upper:]' '[:lower:]'
Output:
hello world 123
Cleaner than writing A-Z and a-z manually, and more portable across different systems.
tr in Security Workflows
Normalizing Case Before Comparison
When comparing strings or building wordlists, case inconsistency causes missed matches. Normalize everything to lowercase first:
bash
tr '[:upper:]' '[:lower:]' < usernames.txt
Stripping Carriage Returns from Windows Files
Files transferred from Windows systems often have rn line endings instead of n. This breaks many Linux tools because the r shows up as a character at the end of each line.
bash
tr -d 'r' < windows_file.txt
Deletes every carriage return. The file now has clean Unix line endings.
This comes up constantly in CTF work and real engagements — scripts fail, tools behave unexpectedly, and the cause is a hidden r at the end of every line.
Extracting Only Printable Characters from a String
bash
echo "$messy_string" | tr -cd '[:print:]'
[:print:] covers all printable characters. -cd deletes everything else — control characters, null bytes, anything non-printable.
Building a Simple ROT13 Decoder
ROT13 is a basic Caesar cipher that shifts letters by 13. It appears in CTF challenges and occasionally in obfuscated data.
bash
echo "Uryyb Jbeyq" | tr 'A-Za-z' 'N-ZA-Mn-za-m'
Output:
Hello World
Breaking down the character mapping:
A-Zmaps toN-ZA-M— shifts uppercase letters by 13a-zmaps ton-za-m— shifts lowercase letters by 13
tr handles the full substitution in one pass. No script needed.
Converting Colons to Newlines for Readable Output
PATH variables, hash files, and other colon-delimited data are hard to read in one long line:
bash
echo $PATH | tr ':' 'n'
Output:
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
One entry per line. Readable. Useful for quickly auditing PATH entries during privilege escalation recon.
Cleaning Hash Output for Cracking Tools
Some tools output hashes with extra characters — colons, brackets, labels. Cracking tools like hashcat want clean input. tr strips the noise:
bash
tr -d '[]' < hashes.txt | cut -d':' -f2
tr removes brackets. cut extracts the hash field. Clean input for the next stage.
Part Three — cut and tr Together
Neither tool is an island. They chain naturally.
Normalize Then Parse
bash
tr -s ':' < /etc/passwd | cut -d':' -f1,7
tr -s ':' squeezes any accidental repeated colons. cut then extracts usernames and shells cleanly.
Extract and Clean in One Pipeline
bash
grep "open" nmap.txt | cut -d'/' -f1 | tr -d ' '
grep finds open port lines. cut extracts the port number field. tr strips any trailing spaces. Clean port list, ready for the next tool.
Build a Username List from Full Names
If you have a file of full names like John Smith:
bash
tr '[:upper:]' '[:lower:]' < names.txt | tr ' ' '.'
Converts John Smith to john.smith. A common username format. Fast wordlist generation from a names dump.
Decode a Colon-Separated Credential File
bash
cut -d':' -f1,2 creds.txt | tr ':' ' '
cut extracts the first two fields (username and password). tr replaces the colon with a space. Clean, readable output — or a format ready for another tool.
Quick Reference
cut
| Flag | What It Does |
|—-|—-|
| -d 'x' | Set the delimiter to character x |
| -f n | Extract field n |
| -f n,m | Extract fields n and m |
| -f n-m | Extract fields n through m |
| -f n- | Extract from field n to end of line |
| -f -n | Extract from start of line to field n |
| -c n | Extract character at position n |
| -c n-m | Extract characters n through m |
| --output-delimiter='x' | Use x as the separator in output |
tr
| Flag / Usage | What It Does |
|—-|—-|
| tr 'set1' 'set2' | Replace each char in set1 with corresponding char in set2 |
| tr -d 'set' | Delete every character in set |
| tr -s 'set' | Squeeze consecutive repeated characters in set to one |
| tr -c 'set' 'x' | Complement — replace everything NOT in set with x (used with -d or -s) |
| tr -cd 'set' | Delete everything NOT in set |
| tr -cs 'set' 'n' | Collapse runs of non-set characters into single newlines |
POSIX Character Classes for tr
| Class | Matches |
|—-|—-|
| [:alpha:] | All letters |
| [:digit:] | Digits 0-9 |
| [:alnum:] | Letters and digits |
| [:upper:] | Uppercase letters |
| [:lower:] | Lowercase letters |
| [:space:] | Whitespace |
| [:punct:] | Punctuation |
| [:print:] | All printable characters |
Closing
cut and tr do not look impressive on their own. cut extracts a column. tr swaps some characters.
But in a pipeline, they are the tools that turn raw output into usable input. grep finds the lines. cut pulls the field you need. tr cleans up what is left. By the time data reaches the next tool in the chain, it is exactly the shape it needs to be.
Quiet, fast, and almost always necessary.