Bash
帶引號與不帶引號的字元串擴展
for i in $(xrandr); do echo "$i" ; done
for i in "$(xrandr)"; do echo "$i"; done
for i in "$(xrandr)"; do echo $i; done
我明白為什麼 1 與 2 不同。但為什麼 3 的輸出與 2 不同?請解釋輸出。引號如何在換行符上起作用?
未加引號的變數(如
$var
)或命令替換(如$(cmd)
orcmd
)是Bourne-like shell 中的split+glob運算符。也就是說,它們的內容根據
$IFS
特殊變數的目前值(預設包含空格、製表符和換行符)進行拆分然後,該拆分產生的每個單詞都受文件名生成(也稱為globbing或文件名擴展)的影響,也就是說,它們被視為模式並被擴展為與該模式匹配的文件列表。
所以在
for i in $(xrandr)
中$(xrandr)
,因為它不在引號內,所以在空格、製表符和換行符的序列上被分割。並且檢查分割產生的每個單詞是否匹配文件名(或者如果它們不匹配任何文件,則保持原樣),並for
循環遍歷它們。在
for i in "$(xrandr)"
中,我們沒有使用 split+glob 運算符,因為引用了命令替換,因此在循環中對一個值進行了一次傳遞:(沒有命令替換xrandr
去除的尾隨換行符)的輸出。但是 in
echo $i
,$i
再次未引用,因此 的內容再次$i
被拆分並受文件名生成的影響,並且這些內容作為單獨的參數傳遞給echo
命令(並echo
輸出由空格分隔的參數)。所以吸取了教訓:
- 如果您不想分詞或生成文件名,請始終引用變數擴展和命令替換
- 如果您確實想要分詞或生成文件名,請將它們不加引號但進行
$IFS
相應設置和/或在需要時啟用或禁用文件名生成 (set -f
,set +f
)。通常,在上面的範例中,如果您想遍歷 輸出中的空格分隔的單詞列表
xrandr
,您需要:
- 保留
$IFS
其預設值(或取消設置)以在空白處拆分- 用於
set -f
禁用文件名生成,除非您確定xrandr
永遠不會輸出任何*
或?
或[
字元(這是文件名生成模式中使用的萬用字元)然後只在循環
in
部分使用 split+glob 運算符(只保留命令替換或變數擴展不加引號) :for
set -f; unset -v IFS for i in $(xrandr); do whatever with "$i"; done
如果要循環輸出的(非空)行,則
xrandr
需要設置$IFS
為換行符:IFS=' '