tr & xargs β Transform and Execute#
tr β Translate Characters#
tr reads stdin and translates, squeezes, or deletes characters. It operates on individual bytes/characters, not lines.
Syntax#
tr [OPTIONS] SET1 [SET2]
| Option | Meaning |
|---|---|
-d SET | Delete characters in SET |
-s SET | Squeeze consecutive chars in SET to one |
-c SET | Complement (all chars NOT in SET) |
-C SET | Complement for multibyte chars |
Translation (SET1 β SET2)#
echo "Hello World" | tr 'a-z' 'A-Z' # lowercase β uppercase
echo "Hello World" | tr 'A-Z' 'a-z' # uppercase β lowercase
echo "hello" | tr 'aeiou' '*' # replace vowels
echo "abc" | tr 'abc' 'xyz' # map aβx, bβy, cβz
# ROT13
echo "secret" | tr 'A-Za-z' 'N-ZA-Mn-za-m'
Delete characters#
echo "hello 123" | tr -d '0-9' # remove digits
echo "line\r" | tr -d '\r' # remove carriage returns (CRLF β LF)
echo "abc xyz" | tr -d ' ' # remove all spaces
echo "a,b,,c" | tr -d ',' # remove commas
cat file | tr -d '\n' # join all lines (remove newlines)
Squeeze repetitions#
echo "hello world" | tr -s ' ' # collapse spaces β single space
echo "aabbcc" | tr -s 'a-z' # squeeze each run of repeated letters
echo "line1\n\nline3"| tr -s '\n' # remove blank lines
Complement#
# -c SET means "all characters NOT in SET"
echo "abc 123 !?" | tr -cd '0-9\n' # keep only digits (and newlines)
echo "abc 123" | tr -c '[:alnum:]' '_' # replace non-alphanumeric with _
Character classes#
| Class | Characters |
|---|---|
[:alpha:] | Letters |
[:lower:] | Lowercase letters |
[:upper:] | Uppercase letters |
[:digit:] | Digits 0β9 |
[:alnum:] | Letters + digits |
[:space:] | Whitespace (space, tab, newlineβ¦) |
[:blank:] | Space and tab only |
[:punct:] | Punctuation |
[:print:] | Printable characters |
tr '[:lower:]' '[:upper:]' # canonical case conversion
tr -d '[:punct:]' # strip punctuation
tr -s '[:space:]' '\n' # split words onto separate lines
tr -cd '[:print:]\n' # strip non-printable characters
Practical tr recipes#
# Convert Windows CRLF to Unix LF
tr -d '\r' < windows.txt > unix.txt
# Tokenise text into one word per line
tr -sc '[:alpha:]' '\n' < file.txt
# Count word frequency (combined with sort/uniq)
tr -sc '[:alpha:]' '\n' < essay.txt | tr '[:upper:]' '[:lower:]' \
| sort | uniq -c | sort -rn | head -20
# Generate a simple password (alphanumeric only)
tr -dc '[:alnum:]' < /dev/urandom | head -c 16
# Remove all blank lines
tr -s '\n' < file.txt
# Convert tabs to spaces (4-space equivalent)
expand -t 4 file.txt # better use expand; tr can't count
xargs β Build and Execute Commands#
xargs reads items from stdin (delimited by whitespace or NUL) and passes them as arguments to a command.
Syntax#
xargs [OPTIONS] [CMD [INITIAL-ARGS]]
Basic usage#
echo "a b c" | xargs echo "args:" # args: a b c
cat files.txt | xargs rm # delete listed files
find . -name "*.log" | xargs wc -l # count lines in all logs
ls *.txt | xargs grep "pattern" # search all .txt files
NUL safety (handles spaces in filenames)#
# Always prefer -0 / -print0 combination
find . -name "*.txt" -print0 | xargs -0 grep "pattern"
fd -0 -e log | xargs -0 rm
printf '%s\0' "file 1.txt" "file 2.txt" | xargs -0 cat
Limit arguments per call#
echo "a b c d e" | xargs -n 2 echo # 2 args per call
# β echo a b
# β echo c d
# β echo e
cat urls.txt | xargs -n 1 curl -LO # download one URL at a time
Parallel execution#
# -P N β run up to N processes in parallel
find . -name "*.png" -print0 | xargs -0 -P 8 -n 1 convert {} {.}.webp
# Compress files in parallel
find . -name "*.log" -print0 | xargs -0 -P 4 -n 1 gzip
# Run tests in parallel
cat test_list.txt | xargs -P $(nproc) -n 1 ./run_test.sh
Placeholder {}#
cat files.txt | xargs -I {} cp {} {}.bak # backup each file
cat hosts.txt | xargs -I {} ssh {} "uptime" # ssh to each host
find . -name "*.gz" | xargs -I {} -n 1 gunzip {} # decompress each
# Use a different placeholder name
cat list.txt | xargs -I FILE sh -c 'echo "Processing FILE"; wc -l FILE'
Interactive confirmation#
ls *.tmp | xargs -p rm # prompt before each call
ls *.bak | xargs -t rm # trace: print command before running
Handle empty input#
# Without -r, xargs runs CMD with no args if stdin is empty
# With -r, it does nothing if stdin is empty
find . -name "*.bak" | xargs -r rm
Combine with shell#
# When you need shell features (pipes, ;, &&)
cat files.txt | xargs -I {} sh -c 'wc -l {} && echo "---"'
# Process with a function
process() { echo "Handling: $1"; }
export -f process
cat items.txt | xargs -P4 -n1 bash -c 'process "$@"' _
Practical xargs recipes#
# Find and delete old logs
find /var/log -name "*.log.gz" -mtime +90 -print0 | xargs -0 rm -v
# Mass rename: add prefix
ls *.txt | xargs -I {} mv {} prefix_{}
# Check if all listed packages are installed
cat packages.txt | xargs dpkg -l 2>/dev/null | grep "^ii"
# Download a list of URLs
cat urls.txt | xargs -n 1 -P 5 wget -q
# Run linter on all changed files
git diff --name-only HEAD | grep '\.py$' | xargs -r flake8
# Copy matched files preserving directory structure
find src/ -name "*.conf" -print0 \
| xargs -0 -I {} install -D {} "/backup/{}"
# Batch API call (1 item at a time, 4 parallel workers)
cat ids.txt | xargs -P 4 -n 1 -I ID curl -s "https://api.example.com/item/ID"
[!TIP] Always use
find -print0 | xargs -0(NUL delimiters) rather than newline-delimited output when filenames may contain spaces, tabs, or newlines. This is especially important in scripts running against user-supplied directories.
[!TIP]
parallel(GNU Parallel) is a powerfulxargs -Preplacement with progress bars, job logging, and advanced input templating. Install withapt install parallel; same-j Nflag for concurrency.