A log file gives you too much information at once. A CSV has its columns in the wrong order. Two lists must be put side by side before being processed. In these situations, graphical tools are no help: cut, tr and paste do the job in a single command line.
These three tools belong to the family of Unix filters. They read text, transform it and write it, with no intermediate file, no interface, fully composable through pipes. cut extracts, tr replaces, paste assembles. Together, they prepare data for other tools (sort, awk, sed, Bash scripts) with an efficiency that is hard to beat.
You have seen cut and tr in the fundamentals. This guide goes further: advanced options, real administration use cases, frequent mistakes and practical combinations.
What you will learn
- Extract multiple fields, ranges and complements with
cut - Convert character sets, delete and squeeze with
tr - Merge several files or streams into columns with
paste - Combine these three tools in common administration pipelines
In which context?
You rarely handle text for fun. You do it because a tool gives you more than you need, and you must extract the essentials before passing them to another tool. In practice: you get a CSV export of an inventory with 20 columns and only need 3; an application log mixes upper and lower case and your comparison script is case-sensitive; two reference files must be merged side by side before being loaded into a report. These three situations come up every week in administration or CI/CD work, and cut, tr, paste settle them in one line, without a temporary file or a Python script.
cut, extract precisely
cut splits each line and keeps only the requested portion. It works in two exclusive modes: by field (-f), with a single-character delimiter set by -d, or by character position (-c). The default delimiter is the tab, which explains most of the empty outputs seen on a file separated by colons or commas.
Reminder of the basic syntax
These two forms cover most uses, one by field, the other by position.
cut -d: -f1 /etc/passwd # field 1, delimiter :cut -c1-10 file.txt # characters 1 to 10If these two lines are not familiar, start with the Filter and transform text guide in the fundamentals.
Extract several fields and ranges
cut accepts a list of comma-separated fields, or a range with a dash:
# Fields 1, 3 and 5 of the passwd filecut -d: -f1,3,5 /etc/passwd
# Fields 1 to 3 (range)cut -d: -f1-3 /etc/passwd
# From field 4 to the end of the linecut -d: -f4- /etc/passwd
# From the first character to the 5thcut -c-5 file.txtInvert the selection with --complement
The --complement option selects everything except the fields given. It is only available in GNU cut (Linux):
# Everything except field 2 (the password) in /etc/passwdcut -d: --complement -f2 /etc/passwdIt is useful to quickly anonymise a sensitive column in a file without rewriting the others.
Change the output separator
By default, cut reuses the same delimiter on output. GNU cut picks another one with --output-delimiter, which directly gives CSV output from a colon-delimited file:
cut -d: -f1,3,6 --output-delimiter=',' /etc/passwdOn a cut that is not GNU (macOS, BSD), the option does not exist: you then convert the separator afterwards with tr, cut -d: -f1,3,6 /etc/passwd | tr ':' ',', which produces the same output.
root,0,/rootbob,1000,/home/bobThe limits of cut
cut works on fixed positions: it is not suited when columns have variable widths (multiple spaces). In that case, awk is more powerful.
# Wrong result if several spaces separate the columnsps aux | cut -d' ' -f3 # unpredictable result with multiple spaces
# Better with awkps aux | awk '{print $3}' # always correcttr, convert and clean
tr (translate) reads from standard input and transforms or deletes characters. It never works with files directly, always through a pipe or a redirection.
Convert character sets
The basic syntax: tr 'set1' 'set2'
# Upper case → lower caseecho "CRITICAL ERROR" | tr 'A-Z' 'a-z'
# Lower case → upper casecat /etc/hostname | tr 'a-z' 'A-Z'
# Replace spaces with underscores (file names)echo "my log file.txt" | tr ' ' '_'Delete characters with -d
With -d, tr only expects a single set: any character that belongs to it disappears from the stream, the others go through unchanged.
# Delete all the digits of a lineecho "error-404-page-not-found" | tr -d '0-9'
# Delete the Windows carriage returns (\r) of a file copied from Windowstr -d '\r' < windows-file.txt > unix-file.txt
# Delete spacesecho " root : 0 " | tr -d ' 'Squeeze repetitions with -s
The -s (squeeze) option reduces consecutive repetitions of a character to a single occurrence:
# Squeeze multiple spaces into oneecho "root : 0 : /root" | tr -s ' 'root : 0 : /root# Squeeze the empty lines of a file (several consecutive \n)tr -s '\n' < file-with-empty-lines.txtHandy to normalise the output of commands such as df, ps or netstat, which use variable spacing.
Use the POSIX character classes
tr accepts POSIX classes between [: and :]:
tr '[:upper:]' '[:lower:]' # same as A-Z → a-z, without accented letterstr -d '[:space:]' # delete all whitespace (space, tab, newline...)tr -d '[:punct:]' # delete all punctuationtr -d '[:digit:]' # delete the digitsTake care to use single quotes around strings containing !, # or $ to avoid interpretation by the shell:
echo 'abc123!@#' | tr -d '[:digit:]' # → abc!@#echo 'abc123!@#' | tr -d '[:punct:]' # → abc123Administration examples
Three recurring cases illustrate the real uses of tr: normalising a separator, extracting a URL segment in an Apache log, and putting a vertical list back on a single line to pass it to a command. Note the < export.csv redirection in the first example: tr never takes a file name as an argument.
# Normalise a CSV file exported from Excel (separator ; → ,)tr ';' ',' < export.csv > export-normalised.csv
# Extract the first path segment of the URLs in an Apache log# (combined format: ... "GET /blog/page HTTP/1.1" ...)grep 'GET' /var/log/apache2/access.log | cut -d'"' -f2 | cut -d' ' -f2 | cut -d'/' -f2 | sort -u
# Prepare a list of packages (one per line → a single line for apt install)cat packages.txt | tr '\n' ' 'paste, merge files into columns
paste is less known than cut and tr, but very useful to assemble several files or streams side by side.
Merge two files
Imagine two files:
alicebobcharlieadminuseruserpaste names.txt roles.txtalice adminbob usercharlie userBy default, paste separates the columns with a tab.
Change the delimiter with -d
The option replaces the tab with the character of your choice, which is enough to produce CSV directly.
paste -d',' names.txt roles.txtalice,adminbob,usercharlie,userYou can chain several different delimiters to assemble three files or more:
paste -d':,' names.txt roles.txt uid.txtpaste alternates between : and , at each field.
Turn lines into columns with -s
The -s (serial) option merges all the lines of a file into a single line:
paste -s names.txtalice bob charlieIt is the equivalent of tr '\n' '\t', with control over the delimiter and one difference: paste ends the line with a newline, where tr leaves a trailing separator.
# Generate an inline list for a commandpaste -sd',' names.txtalice,bob,charlieNumber each line
Combine paste with the nl command (or cat -n) to number lines:
nl -ba names.txt | paste -d' ' - roles.txtOr more simply:
paste <(nl -ba names.txt) roles.txtAssemble the output of two commands
By combining paste with two process substitutions, you put the result of two commands side by side without writing any intermediate file.
# Compare the local users with their UIDspaste <(cut -d: -f1 /etc/passwd) <(cut -d: -f3 /etc/passwd)root 0daemon 1bob 1000Common administration pipelines
The five chains below match real operational needs. They all follow the same progression: a command produces too wide an output, tr normalises it, cut extracts the useful column, and a last filter sorts or deduplicates. Always test a pipeline step by step, removing the last link, before freezing it in a script.
Extract the IPs of the active SSH connections
This pipeline assumes an ss output whose columns are separated by a variable number of spaces, hence the mandatory tr -s.
ss -Htn state established '( sport = :22 )' | tr -s ' ' | cut -d' ' -f4 | cut -d: -f1 | sort -uss -Htn state established '( sport = :22 )': established TCP connections on local port 22, the SSH server's, without a header line (-H), otherwise the wordAddressends up in the results. Without the port filter, the pipeline would list every established connection. Beware,ssshortens the state toESTAB: agrep ESTABLISHEDwould never return anything. The nativestate establishedfilter is safer than a grep.tr -s ' ': normalises multiple spacescut -d' ' -f4: extracts the remote address column. Thestatefilter removes the state column from the output, so the remote address becomes the fourth field, not the fifth.cut -d: -f1: removes the port. Valid for IPv4 addresses: an IPv6 address itself contains:sort -u: deduplicates
Create a CSV file from /etc/passwd
The fields kept are the login name, the UID and the home directory; tr then converts the historical : separator into a comma.
cut -d: -f1,3,6 /etc/passwd | tr ':' ',' > users.csvCompare two lists and find the differences
comm requires two sorted inputs, hence the sort in the substitutions. The -13 options hide columns 1 and 3, leaving only the lines present only in the second file, in other words the packages added.
# Two sorted lists of installed packages (before/after)comm -13 <(sort before.txt) <(sort after.txt)Clean a script generated on Windows
A \r at the end of the shebang causes the bad interpreter error, a fault nothing makes visible on screen. The redirection writes a new file: the original script stays intact, and the execution permission has to be set again on the copy.
tr -d '\r' < script.sh > script-clean.shchmod +x script-clean.shGenerate an inline list of services to start
paste -sd' ' puts the service names on a single line, which xargs then passes as arguments to systemctl. Check the content with an echo before really running the command: xargs waits for no confirmation.
cat services.txt | paste -sd' ' | xargs systemctl startTroubleshooting
Most surprises with these three tools come from characters that are invisible on screen: a tab taken for a space, a \r at the end of a line, multiple spaces between columns. The first reflex is therefore cat -A file, which makes these characters visible. The lines below cover the seven most frequent symptoms.
| Problem | Likely cause | Solution |
|---|---|---|
cut returns empty fields | Wrong delimiter | Check with cat -A file, tabs show as ^I |
cut --complement not recognised | Non-GNU system (macOS, BSD) | Use awk instead |
tr: tr: extra operand | Too many arguments | tr takes only two sets, check the syntax |
tr: accented letters not transformed | GNU tr works byte by byte, UTF-8 escapes it | Use GNU sed: sed 's/.*/\L&/' (lower case) or \U (upper case) |
paste: shifted columns | Files with a different number of lines | Check with wc -l, paste fills with empty fields if they differ |
paste: unwanted tabs | Default delimiter | Add -d',' or -d' ' to choose |
Nothing shows with tr | Forgotten pipe or redirection | tr reads stdin, use echo "..." | tr ... or tr ... < file |
Put it into practice
Turning a raw file into exact facts is the daily routine of administration scripting. This lab makes you cut columns, deduplicate lines, count occurrences, sum a field and rewrite a delimiter with cut, sort, uniq, sed and awk. Your output is compared with the expected result, line for line.
Key points
cutextracts columns by position or by delimiter.-f4-selects from field 4 to the end.--complementinverts the selection and--output-delimiterchanges the output separator (GNU only).trtransforms byte by byte from stdin.-ddeletes,-ssqueezes repetitions. POSIX classes ([:upper:],[:digit:]) are more readable than ranges, but none converts accented letters: for those, GNUsed(\L,\U).pasteassembles files into columns.-dcontrols the delimiter,-smerges all the lines of a file onto a single line.- These three tools are designed for the pipe: they never modify the source files.
- When columns have variable widths (multiple spaces), prefer
awktocut.
Knowledge check
Check that the essentials of this guide stuck. The questions cover only what has just been explained here.
Knowledge check
Check what you have learned with this interactive quiz
How it works
- The timer starts when you click Start
- Multiple choice, true/false and short answer questions
- You can move between questions
- Detailed results are shown at the end
Starts the quiz and the timer
Review
(0/0)Score by domain
A domain groups several guides and gets several questions, so its score is more reliable than a per-topic score, which often rests on a single question.
Summary
Strengths
No topic passed at 80% on at least two questions yet.
Weak areas
No topic below the pass mark.
Recommended next
Hints before you try again?
Continue the course: Sort, count and deduplicate on Linux
A new full quiz with random questions
Work again only on the questions you missed