skip to content

tr & xargs β€” Transform and Execute

Translate, squeeze, and delete characters with tr. Build and execute command lines from stdin with xargs. Includes parallel execution, NUL safety, and pipeline recipes.

6 min read 17 snippets 2d ago intermediate

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]
OptionMeaning
-d SETDelete characters in SET
-s SETSqueeze consecutive chars in SET to one
-c SETComplement (all chars NOT in SET)
-C SETComplement 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#

ClassCharacters
[: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 powerful xargs -P replacement with progress bars, job logging, and advanced input templating. Install with apt install parallel; same -j N flag for concurrency.