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

スペースを含む変数のみが引用符で囲まれます。

一重引用符が追加されないようにするにはどうすればよいですか?

引用符/エスケープをまったく使用していない場合、シェルを開くときにエラーが発生します。

/.zshrc:export:11: このコンテキストでは無効です: -Dskip2

答え1

ここでは環境変数を(ある意味)乱用していると思います。そのスペースは変数の一部であり、 でパラメータ展開を実行すると$、そのスペースがコマンドに渡されるだけです。mvnは、変数の内容という 1 つの引数を取得します。

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

あなたが望むことをするための一つの選択肢はグローバルエイリアス(これも Bash ではなく Zsh の機能です)、行内の任意の場所で置換できます。スペースは保持されます。

$ alias -g SKIP="-Dskip1 -Dskip2"
$ alias -g SKIP2="-Dskip3"
$ mvn SKIP SKIP2

これにより、追加された場所に文字列が単に「追加」されます。

答え2

この問題は引用符やエスケープとは関係なく、単語の分割(またはその欠如)に関係しています。ほとんどのシェルでは、空白を含む変数を二重引用符で囲まずに使用すると、その値はその空白に基づいて「単語」に分割されます。したがって、変数がSKIPに設定され-Dskip1 -Dskip2(この値には引用符やエスケープがないことに注意してください)、 のように使用するとmvn $SKIP、 と同等になりますmvn "-Dskip1" "-Dskip2"

しかし、zshはほとんどのシェルとは異なり、(デフォルトでは)単語分割を行いません。変数が に設定されている場合、-Dskip1 -Dskip2それがコマンドに渡されます。単一の議論として出力に表示される引用符はset -x実際には存在せず、文字列全体が単一の引数として渡されることを明示的にするために追加されているだけです。

zsh に単語分割を行うように指示する方法はいくつかあります。=展開時に修飾子を使用できます。

mvn ${=SKIP}

または、シェル オプションを使用して、すべての展開に対して sh のような分割をオンにすることもできます。

setopt shwordsplit
mvn $SKIP

...ただし、これには、単一の引数だと思っていたものが予期せず分割されたり、ファイル名のワイルドカードのように見えるものがファイル名のリストに拡張されたりするなど、好ましくない副作用が生じる可能性があります。シェルの単語分割はバグの原因になる傾向があるため、zsh ではデフォルトで無効になっています。変数に複数の文字列を格納するための本当に適切な方法は、単純な変数ではなく配列を使用することです。

SKIP=(-Dskip1 -Dskip2)
mvn "${SKIP[@]}"

これは、zsh、bash、およびその他のシェルで機能します (ただし、配列をサポートしていないシェルでは機能しません)。ただし、zsh から配列をエクスポートすることはできません ( を実行することはできますexport SKIPが、何も実行されません)。配列はシェルの機能ですが、環境変数はプレーンな文字列のみをサポートする OS の機能です。これらのオプションを本当にエクスポートする必要がありますか、それともシェル内でのみ使用していますか?

答え3

のため、単一引用符が診断行に表示されますset -x。これらは には到達しませんmvn。変数コンテンツ ( ) は、 では変数を厳密に引用符で囲む必要がないため (POSIX 準拠のシェルではそうする必要があります)、単一の引数として に-Dskip1 -Dskip2到達します。mvnzsh

問題は変数を展開する方法にあるようですzsh。引用符で囲まれていないときに単語分割が行われるようにしたいのだと思います$SKIP他のシェルでも起こるように) ですが、zsh通常は発生しません。

${=SKIP}に使用置換後に単語分割をトリガーする

例:

% SKIP='-Dskip1 -Dskip2' 
% printf "%s\n" $SKIP   
-Dskip1 -Dskip2
% printf "%s\n" ${=SKIP}
-Dskip1
-Dskip2
% 

関連情報