新增一個包含空格的環境變數~/.zshrc
export SKIP="-Dskip1 -Dskip2"
export SKIP_NO_SPACES="-Dskip3"
嘗試使用它
set -x
mvn $SKIP $SKIP_NO_SPACES
正在執行的命令實際上是
+-zsh:108> mvn '-Dskip1 -Dskip2' -Dskip3
只有包含空格的變數才會被引用。
如何避免加上單引號?
如果根本不使用引號/轉義,打開 shell 時會出現錯誤:
/.zshrc:export:11:在此上下文中無效:-Dskip2
答案1
我認為你在這裡(有點)濫用環境變數。該空格是變數的一部分,當您使用 執行參數擴充時$
,它只會將該空格傳遞給指令。mvn
得到一個參數——你的變數內容。
正如 Kamil Maciorowski 在評論中正確觀察到的那樣,這是 Zsh 特有的。如果您省略引號,您所提議的內容將在 Bash 中起作用:
bash-5.0$ export test="foo bar"
bash-5.0$ ls $test
ls: bar: No such file or directory
ls: foo: No such file or directory
bash-5.0$ ls "$test"
ls: foo bar: No such file or directory
做你想做的事情的一個選擇是全域別名(也是 Zsh 的功能,而不是 Bash),可以在一行中的任何位置替換。它保留其空間:
$ alias -g SKIP="-Dskip1 -Dskip2"
$ alias -g SKIP2="-Dskip3"
$ mvn SKIP SKIP2
這將簡單地“附加”字串到添加的位置。
答案2
此問題與引用或轉義無關,而是與分詞(或缺乏分詞)有關。在大多數 shell 中,如果您使用包含空格的變數而不用雙引號將其括起來,則該值將根據該空格拆分為「單字」。因此,如果變數SKIP
設定為-Dskip1 -Dskip2
(請注意,該值中沒有引號或轉義符),並且您像 一樣使用它mvn $SKIP
,那麼它就相當於mvn "-Dskip1" "-Dskip2"
。
但 zsh 不是大多數 shell,並且(預設)它不進行分詞。如果變數設定為-Dskip1 -Dskip2
,那麼這就是傳遞給命令的內容,作為單一參數。您在輸出中看到的引號set -x
實際上並不存在,它們只是被添加以明確整個字串作為單一參數傳遞。
有幾種方法可以告訴 zsh 您想要完成分詞。您可以=
在擴充功能中使用修飾符:
mvn ${=SKIP}
或者,您可以使用 shell 選項為所有擴充功能啟用類似 sh 的分割:
setopt shwordsplit
mvn $SKIP
……但是,這可能會產生令人不快的副作用,例如意外拆分您認為是單一參數的內容,以及將任何看起來像檔案名稱通配符的內容擴展為檔案名稱清單。 Shell 分詞往往會導致錯誤,這就是 zsh 中預設禁用它的原因。在變數中儲存多個字串的真正正確方法是使用陣列而不是普通變數:
SKIP=(-Dskip1 -Dskip2)
mvn "${SKIP[@]}"
這適用於 zsh、bash 和其他一些 shell(但不適用於那些不支援陣列的 shell)。但是,您無法從 zsh 匯出陣列(當然,您可以運行export SKIP
,但它不會執行任何操作)。陣列是 shell 功能,而環境變數是作業系統的東西,只支援純字串。您真的需要匯出這些選項,還是只是在 shell 中使用它們?
答案3
由於 而列印的診斷行中出現單引號set -x
。他們沒有得到mvn
。變數內容 ( -Dskip1 -Dskip2
) 作為mvn
單一參數,因為zsh
您不需要嚴格引用變數(在 POSIX 相容的 shell 中需要這樣做)。
問題似乎出在zsh
擴展變數的方式。我認為你希望在未加引號時進行分詞$SKIP
(就像發生在其他 shell 中一樣)但zsh
通常不會發生。
${=SKIP}
用於替換後觸發分詞。
例子:
% SKIP='-Dskip1 -Dskip2'
% printf "%s\n" $SKIP
-Dskip1 -Dskip2
% printf "%s\n" ${=SKIP}
-Dskip1
-Dskip2
%