Bash

帶引號與不帶引號的字元串擴展

  • March 17, 2017
  1. for i in $(xrandr); do echo "$i" ; done
  2. for i in "$(xrandr)"; do echo "$i"; done
  3. for i in "$(xrandr)"; do echo $i; done

我明白為什麼 1 與 2 不同。但為什麼 3 的輸出與 2 不同?請解釋輸出。引號如何在換行符上起作用?

未加引號的變數(如$var)或命令替換(如$(cmd)or cmd)是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='
'

引用自:https://unix.stackexchange.com/questions/78914