如何正確匯出ZSH中包含空格的環境變數

如何正確匯出ZSH中包含空格的環境變數

新增一個包含空格的環境變數~/.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
% 

相關內容