軌道
/
Bash
Bash
/
課程大綱
/
Here 文件
He

Here 文件 在 Bash

{other: "%{count} 個練習"}

關於 Here 文件

在 Bash 指令碼中,「here document」(或稱「heredoc」)會把多行輸入重新導向到某個指令或程式,就像你直接在終端機裡輸入這些內容一樣。 這是一項強大的工具,能讓你在指令碼中內嵌多行文字,而不需要外部檔案,也不必做複雜的字串處理。

主要特色與語法

  1. 分隔符號:heredoc 以<<運算子開頭,後面接著一個作為分隔的詞(常稱為「標記」或「終止符」)。 這個分隔詞可以是任何你選的字詞,但為了清楚起見,通常會用像EOF、END或TEXT這類詞。 想讓程式碼更好讀的話,你可以用更具描述性的分隔詞,例如END_INSTALLATION_INSTRUCTIONS。

  2. 內容:在開頭的<< DELIMITER之後,寫下你想重新導向的內容。 這可以是多行文字、程式碼,或任何其他內容。

  3. 結束:當分隔詞再次單獨出現在一行,且前後都沒有空白時,heredoc 就結束了。

基本語法

command << DELIMITER
  Content line 1
  Content line 2
  ...
  Content line N
DELIMITER

運作方式

  • Bash 會讀取開頭<< DELIMITER與結尾DELIMITER之間的所有行。
  • Bash 會把這些內容連接到指令的標準輸入。
  • 指令會處理這些輸入,就像它們是從鍵盤打進來的一樣。

範例 1:簡單的文字輸出

cat << EOF
This is the first line.
This is the second line.
This is the third line.
EOF

輸出:

This is the first line.
This is the second line.
This is the third line.

在這個範例中:

  • cat是指令。
  • << EOF會啟動 heredoc,並以EOF作為分隔詞。
  • 這三行文字就是內容。
  • 單獨一行的EOF會結束 heredoc。
  • 接著cat會輸出它收到的內容。

範例 2:搭配 wc(字數統計)

wc -l << END
Line 1
Line 2
Line 3
END

輸出:

3

這裡的wc -l會計算行數。 heredoc 則提供這三行作為輸入。

範例 3:把資料傳給指令碼

指令碼:

#!/usr/bin/env bash

# Script to process input
while IFS= read -r line; do
  echo "Processing: $line"
done

在互動式的 bash 提示字元中用 heredoc 呼叫這個指令碼:

./your_script << MY_DATA
Item 1
Item 2
Item 3
MY_DATA

輸出:

Processing: Item 1
Processing: Item 2
Processing: Item 3

變化與進階功能

常值內容

Bash 會在 heredoc 中執行變數展開、指令替換與算術展開。 就這點來說,heredoc 的行為就像雙引號字串。

cat << EOF
The value of HOME is $HOME
The current date is $(date)
Two plus two is $((2 + 2))
EOF

輸出:

The value of HOME is /home/glennj
The current date is Thu Apr 24 13:47:32 EDT 2025
Two plus two is 4

當分隔詞加上引號時(使用單引號或雙引號),這些展開就不會發生。 內容會被視為常值。 這就像單引號字串。

cat << 'EOF'
The value of $HOME is not expanded here.
The result of $(date) is not executed.
Two plus two is calculated by $((2 + 2))
EOF

輸出:

The value of $HOME is not expanded here.
The result of $(date) is not executed.
Two plus two is calculated by $((2 + 2))

移除開頭的定位字元

如果你用<<-(結尾帶連字號)而不是<<,Bash 會移除 heredoc 每一行開頭的定位字元。 當你想在指令碼中把 heredoc 內容縮排,又不想影響輸出時,這就很有用。

# Note, the leading whitespace is tab characters only, not spaces!
# The ending delimiter can have leading tabs as well.
cat <<- END
	This line has 1 leading tab.
	  This line has a leading tab and some spaces.
		This line 2 leading tabs.
	END

輸出時會移除所有開頭的定位字元:

This line has 1 leading tab.
    This line has a leading tab and some spaces.
This line 2 leading tabs.
Caution

作者並不建議這種用法。 雖然它可以提升指令碼的可讀性,

  1. 但一不小心就可能把定位字元換成空白(你的編輯器可能會自動這麼做),而且
  2. 空白和定位字元之間的差異很難看出來。

什麼時候該用 Here Document

  • 多行輸入:當你需要把多行文字提供給某個指令時。
  • 設定檔:在指令碼中內嵌一小段設定內容。
  • 產生程式碼:在指令碼中即時產生程式碼。
  • 指令碼互動:為互動式程式模擬使用者輸入。
  • 避免外部檔案:當你想避免建立暫存檔案時。

一個典型的用法是提供一些說明文字:

#!/usr/bin/env bash

usage() {
    cat << END_USAGE
Refresh database tables.

usage: ${0##*/} [-h|--help] [-A|--no-archive]

where: --no-archive flag will _skip_ the archive jobs
END_USAGE
}

# ... parsing command line options here ...

if [[ $flag_help == "true" ]]; then
  usage
  exit 0
fi

可能的缺點

  • 大量內嵌的文件會讓你的程式碼更難閱讀。 把指令碼和文件分開成不同檔案來部署,可能會更好。
  • Here document 可能會打斷程式碼的閱讀節奏。 當你想傳一些文字給某個程式時,你可能正處於層層巢狀的程式碼中。 相較於周圍的程式碼,heredoc 的縮排可能會顯得突兀。

Here Strings

就像 here document 一樣,here string(或稱「herestring」)也會把輸入提供給指令。 不過,heredoc 是以一整塊文字的形式提供,herestring 則是以單一字串的形式提供。 Here string 使用<<< "text"語法。

tr 'a-z' 'A-Z' <<< "upper case this string"

輸出:

UPPER CASE THIS STRING

和 heredoc 不同的是,here string 不需要結尾分隔詞。

為什麼要用 Here String?

你可以用管線來取代 here string:

echo "upper case this string" | tr 'a-z' 'A-Z'

那為什麼還要用 here string 呢?

想想這種情況:某個耗時很久的運算產生了一個字串,而你想把結果分別餵給兩個不同的指令。 用管線的話,你得把這個運算執行兩次:

some_long_running_calculation | first_command
some_long_running_calculation | second_command

更有效率的做法是先把運算的輸出擷取下來(使用指令替換),再用 here string 把輸入提供給後面這兩個指令:

result=$( some_long_running_calculation )
first_command <<< "$result"
second_command <<< "$result"

以下是這個例子在真實世界中的應用:

  • 擷取某個 REST API 查詢(有分頁的那種)的 JSON 回應,
  • 把 JSON 資料交給某個 jq 程式解析結果並輸出到檔案,然後
  • 再把 JSON 資料交給另一個 jq 程式,判斷下一次查詢的 URL。
# initialize the output CSV file
echo "ID,VALUE" > data.csv

url='https//example.com/api/query?page=1'

while true; do
  json=$( curl "$url" )

  # convert the results part of the response into CSV
  jq -r '.results[] | [.id, .value] | @csv' <<< "$json"

  # get the URL for the next page
  url=$( jq -r '.next_url // ""' <<< "$json" )
  if [[ "$url" == "" ]]; then
    break
  fi
done >> data.csv

注意輸出重新導向的位置。 while迴圈的所有輸出都會附加到data.csv這個檔案。

Heredoc 與 Herestring 也是一種重新導向

因為它們只是重新導向的不同形式,所以可以和其他重新導向操作合併使用:

cat <<< END_OF_TEXT > output.txt
This is my important text.
END_OF_TEXT

awk '...' <<< "$my_var" >> result.csv

總結

Here document(或稱「heredoc」)是在 Bash 指令碼中管理多行輸入時,既靈活又方便的方法。 它們簡化了直接把文字和資料內嵌到指令碼裡的流程,讓指令碼更自給自足,也更容易閱讀。

Here string(或稱「herestring」)很像 here document,但提供更簡單、更有彈性的語法。

透過 GitHub 編輯 連結會在新視窗或分頁中開啟