tr 字元轉換的進階技巧 文章首圖

tr 字元轉換的進階技巧

tr 字元轉換的進階技巧

在 Linux 的世界裡,我們經常需要處理純文字資料的轉換與清洗。雖然 sedawk 是強大的文本處理工具,但在處理「字元對字元」的簡單轉換時,tr (translate) 指令往往是最快速且直觀的選擇。許多初學者只知 tr 'a-z' 'A-Z' 這種基礎用法,卻忽略了它強大的集合運算與字元類別功能。這篇文章將帶你深入探索 tr 的進階應用,讓你在處理字串時更加得心應手。

基本觀念:集合與範圍

tr 的核心邏輯是將標準輸入(stdin)中的第一個集合(set1)中的字元,依序對應到第二個集合(set2)中的字元。如果 set2 較短,它會重複 set2 的最後一個字元;如果 set2 較長,多出的部分會被忽略。

最基礎的用法是字元範圍(range)的使用。例如,[a-z] 代表所有小寫英文字母,[0-9] 代表所有數字。

# 將小寫字母轉換為大寫
echo "hello world" | tr 'a-z' 'A-Z'
# 輸出:HELLO WORLD

進階技巧一:字元刪除與補齊

除了轉換,tr 最常用的功能其實是「刪除」字元。這在清理檔案時非常實用。使用 -d (delete) 選項,你可以指定要刪除的字元集合。

# 刪除所有數字
echo "abc123def456" | tr -d '0-9'
# 輸出:abcdef

# 刪除所有空白與換行(將多行合併為一行)
printf "line1\nline2\nline3" | tr -d '\n'
# 輸出:line1line2line3

另一個鮮為人知但極實用的功能是「補齊」(complement)。當你在 set1 中使用 -c 選項時,tr 會將「不在 set1 中」的所有字元視為目標。這在移除特定字元以外的所有內容時非常強大。

# 僅保留字母,移除所有非字母字元(包含數字、標點符號、空白)
echo "Hello, World! 123" | tr -cd 'a-zA-Z'
# 輸出:HelloWorld

進階技巧二:使用 POSIX 字元類別

在 Ubuntu 22.04 或 Debian 12 上,tr 支援 POSIX 字元類別,這讓你的指令更具可讀性與跨平台相容性。常見的類別包括:

  • [:alpha:]:所有字母(a-z, A-Z)
  • [:digit:]:所有數字(0-9)
  • [:space:]:所有空白字元(包含空格、製表符、換行等)
  • [:punct:]:所有標點符號

這比手寫 [a-zA-Z][0-9] 更清晰,特別是當你需要處理 Unicode 或多語言環境時。

# 移除所有標點符號
echo "Hello, World! How are you?" | tr -d '[:punct:]'
# 輸出:Hello World How are you

# 僅保留數字
echo "Price: $99.99" | tr -cd '[:digit:]'
# 輸出:9999

進階技巧三:字元重複與壓縮

有時我們需要將連續重複的字元壓縮成單一字元,或者將字元重複特定次數。

使用 -s (squeeze-repeats) 選項,可以將 set1 中連續出現的字元壓縮為單一實例。這在處理由多個空格或換行組成的檔案時非常有用。

# 將多個連續空格壓縮為單一空格
echo "Hello    World" | tr -s ' '
# 輸出:Hello World

# 將多個連續換行壓縮為單一換行(類似 unexpand 的效果)
printf "line1\n\n\nline2" | tr -s '\n'
# 輸出:line1
#       line2

若你需要將字元重複特定次數,可以使用 -N 選項(部分版本支援)或透過 shell 變數生成。例如,在 Bash 中,你可以這樣做:

# 將 'a' 轉換為 5 個 'x'
echo "a" | tr 'a' "$(printf 'x%.0s' {1..5})"
# 輸出:xxxxx

常見問題

Q1: 為什麼 tr 無法處理 Unicode 或中文?

tr 設計之初是為 8-bit 字元設計的,它並不支援多位元組的 Unicode 字元(如 UTF-8 編碼的中文)。若你需要處理中文或特殊 Unicode 字元,建議改用 sedawk。例如,若要刪除中文標點符號,tr 會失效,必須使用 sed

Q2: 當 set1 和 set2 長度不一致時,行為為何?

這是新手最容易踩的坑。若 set2 較短,tr 會重複 set2 的最後一個字元來補齊。例如:

# set1 有 3 個字元,set2 只有 1 個
echo "abc" | tr 'abc' 'X'
# 輸出:XXX

若 set2 較長,多出的部分會被忽略:

# set1 有 1 個字元,set2 有 3 個
echo "a" | tr 'a' 'XYZ'
# 輸出:X

小結

tr 雖然看似簡單,但其結合 -d-c-s 以及 POSIX 字元類別後,能處理許多日常文字清洗任務。它適合用於管道(pipeline)中的快速轉換,而非複雜的文本結構分析。記住它的限制(如不支援 Unicode),並在合適的場景下使用,你將能大幅提升 Linux 指令行的工作效率。下次當你需要快速清洗字串時,不妨先想想 tr 是否能派上用場。