Bash 可以用一行 sed 或 awk 指令為每一行加上引號,但這個結果只在單一特定情境下才有效:一個沒有內嵌引號、逗號或反斜線的純文字清單。一旦某一行包含目標格式視為特殊字元的字元 —— JSON 中的雙引號、SQL 中的單引號、CSV 中的逗號 —— 這一行指令會靜悄悄地產生損壞的輸出,卻仍能編譯、執行或匯入,只是結果錯誤。JSON 依照 RFC 8259 要求將內嵌的雙引號以反斜線跳脫,SQL 依照 PostgreSQL 與 SQLite 採用的 ISO 規則將單引號重複,CSV 則依照 RFC 4180 將內嵌的雙引號重複。Bash 對這些規則完全沒有內建的理解。區分「包裝一行」與「正確跳脫」之間的差異,是將一個能跑的示範轉為可直接貼上結果的關鍵,而這正是 Add Quotes to Each Line 工具針對每個預設所處理的事。

在 Bash 中將每一行包入引號的常見一行指令
四個 shell 用法就能涵蓋大多數 Bash 中「將每一行包入引號」的工作:sed、awk、while-read 迴圈,以及 xargs。每一種都接收一份清單,每行一個項目,並輸出一份將每行以一組固定引號字元包裝的相同清單。如需更深入的、以 awk 為主的逐步說明,請參閱 awk 為每一行加上引號指南。
最短的 sed 形式使用了 & 元字符,它會展開為整個被匹配的行:
sed 's/.*/"&"/' list.txt > quoted.txt
Awk 則透過一個 print 陳述式,將字面上的引號與 $0(整個目前這一行)串接起來,達到同樣的輸出:
awk '{ print "\"" $0 "\"" }' list.txt > quoted.txt
while-read 迴圈雖然較冗長,但不會為每一行衍生一個子 shell;當輸入有數十萬筆資料時,這一點很重要:
while IFS= read -r line; do printf '"%s"\n' "$line"; done < list.txt > quoted.txt
IFS= 與 -r 旗標很重要:少了它們,前後空白會被去除,而反斜線會被解讀為跳脫序列。xargs 也能完成這項工作,只要用 -I{} 預留位置,一次餵入一行輸入:
xargs -I{} echo '"{}"' < list.txt > quoted.txt
當輸入是乾淨的 ID、名稱或檔名的清單,且沒有內嵌引號、逗號或反斜線時,這四種方式都會產生明顯正確的輸出。問題只有在輸入不再乾淨時才會出現。
Bash 一行指令悄悄出錯的地方
sed 與 awk 形式會將每一行以字面上的引號字元包起來。它們完全不會改變該行的內容;這聽起來無害,直到該行內含目標格式視為特殊的字元。
以一份包含 say "hi" 這行的清單為例。Sed 會將它包成 "say "hi"",這正好是會讓 JSON 解析器壞掉的輸出。Bash 一行指令完全不會察覺哪裡出錯,因為 shell 正愉快地把位元組傳遞過去。真正失敗的是讀取這個檔案的人 —— 通常會在離實際出錯的那行很遠的地方,出現一個語法錯誤。
同樣的問題也會出現在 SQL;當某個值包含單引號(例如 it's)時,必須包成 'it''s'。一個將每行包入單引號的 sed 一行指令會產生 'it's',這會在 it 之後就關閉字串常值,讓該行剩下的部分變成損壞的 SQL。CSV 也有自己的變體:像 3" monitor 這樣的值必須變成 "3"" monitor",內部的引號要重複;否則大多數匯入工具會在內部引號處截斷欄位,或讓欄位錯位。
其他靜悄悄的失敗還包括:空行(會被包成 "",這未必是你想要的)、在引號欄位內變得有意義的前後空白、來源中的反斜線(Windows 路徑與 regex 字串常會包含)、以及 Unicode 字元(某些下游工具讀取後的位元組序列,可能與 sed 所輸出的不同)。這些問題在 Bash 的輸出中完全看不到。它們會在你往下走之後,於某個解析器、查詢或試算表中浮現。
在 Bash 中正確地為每一行加上引號
- 決定輸出要落到哪一種格式:JSON 陣列、SQL IN (...) 子句、CSV 資料列,或其他。每種情況的跳脫規則都不同,選錯就是整個失敗模式。
- 如果清單是髒的 —— 有重複、空行、混雜的空白 —— 先用 Bash 清理它。以下幾行指令能處理大多數情況: sort -u list.txt # 去除重複 awk 'NF' list.txt # 去除空白或只有空白的行 sed 's/^[[:space:]]*//; s/[[:space:]]*$//' list.txt # 修剪每行 將清理後的清單送入下一步。
- 在瀏覽器中開啟 Add Quotes to Each Line。這個工具在你的機器本機上執行 —— 不上傳、不需帳號、頁面載入後不需任何網路往返。
- 將清理後的清單貼到輸入區,每行一個項目。
- 點選符合你需要格式的預設。JSON 陣列會將每行包入雙引號、以逗號連接,並在最外層加上方括號。SQL IN 會將每行包入單引號、以逗號連接,並在最外層加上括號。CSV 資料列會將每行包入雙引號,把任何內嵌的雙引號加倍,並將整列輸出在單行,方便貼入試算表匯入。Plain quotes 則只以你選擇的引號字元包裝每一行,不加上任何外層括號。
- 如果預設的設定不完全符合需求,可以調整控制項:更改引號字元、切換分隔符號、開關跳脫、跳過空行、修剪每行,或為需要結尾分隔符號的格式加上結尾分隔符號。「最後一行不加逗號」是預設行為,對 JSON 與 SQL 也是正確的選擇。
- 用複製按鈕複製結果。工具會回報它處理的行數,你可以和輸入比對,作為健全性檢查。
如果你跑過一次工具後需要不同的設定,請從原本的清單開始,而不是從先前的輸出。對工具自己的輸出再跑一次會再包一層,因為工具無法判斷既有的引號是內容還是先前包裝的結果。
JSON、SQL 與 CSV:三種不同的跳脫規則
同一份輸入在每個預設下會產生不同但正確的輸出。下表整理了每種格式實際要求的內容,依據其所引用的標準而整理。
| 格式 | 引號字元 | 內嵌引號規則 | 輸出包裝 |
|---|---|---|---|
| JSON 陣列 | 雙引號 " | 以反斜線跳脫:" 變成 \",依 RFC 8259 | [ ... ] |
| SQL IN 清單 | 單引號 ' | 引號加倍:' 變成 '',依 PostgreSQL 詞彙規則與 ISO 標準 | ( ... ) |
| CSV 資料列 | 雙引號 " | 引號加倍:" 變成 "",依 RFC 4180 | 單行,以逗號連接 |
| 反引號(JavaScript 樣板字串) | 反引號 ` | 以反斜線跳脫:` 變成 \`,並將插值開頭 ${ 跳脫以避免表達式注入 | 以選擇的分隔符號連接 |
一行包含 say "hi" 的內容,在 JSON 中會變成 "say \"hi\"",在 SQL 中變成 'say "hi"'(雙引號在單引號 SQL 字串中不具特殊意義,但內嵌的單引號要加倍),在 CSV 中則變成 "say ""hi"""。這三條規則彼此不同,而這個差異正是每個預設所編碼的內容。
什麼時候 Bash 不是處理這項工作的合適工具
當輸入是乾淨的、目標也是 Bash、shell 管線或純文字檔案時,Bash 就是合適的工具。傳遞給 rm 或 ls 的檔案路徑清單不需要依格式跳脫,sed 或 awk 一行指令正好是適當的力道。
當清單要落到具有自有跳脫規則的程式碼或資料中時,Bash 就不適合。shell 無法知道 JSON 要求反斜線跳脫的引號、SQL 要求加倍的單引號、或 CSV 要求加倍的雙引號。想用一個巧妙的 sed 運算式同時處理這三種情況,通常會產生六個月後團隊裡沒有人讀得懂的一行指令,包括當初寫它的人。
針對這些情況,Add Quotes to Each Line 工具正是圍繞這個區別而設計。預設與其所引用的標準對齊,因此跳脫是文件記載的行為,而不是手動拼湊出來的。輸入上限為一百萬個字元,並以單一線性處理方式執行,因此即使是非常長的清單也能瞬間回傳。所有處理都在你的瀏覽器中執行,這對那些你不太願意貼到隨機網站的 ID 清單或名單來說,是正確的預設。
Add Quotes to Each Line 中實用的控制項
每個預設都提供相同的底層控制項,讓你不必離開頁面就能調整結果。
- 引號樣式:雙引號、單引號、反引號,或無引號(適用於你只想變更分隔符號的情況)。
- 分隔符號:預設為逗號,但使用不同分隔符號的格式也可以用空白、分號或直線。
- 結尾分隔符號:預設關閉,這樣 JSON 陣列才不會以多餘的逗號結尾。對於需要結尾分隔符號的格式,可將其開啟。
- 跳脫:JSON、SQL 與 CSV 預設為開啟。關閉時,會產生不更動字元的純包裝輸出。
- 跳過空行:去除空白或只有空白的行,避免它們在輸出中變成多餘的空項目。
- 修剪每行:移除每行的前後空白,當來源清單是從表格或聊天訊息複製貼上時特別有用。
工具會回報它處理的行數,這是最簡便的方式來確認沒有任何輸入被悄悄丟棄。如果來源清單含有重複、空行或散亂空白,請先清理來源,再將清理後的輸出送入 Add Quotes to Each Line。每一層都應該把一件事做好。