web-dev-qa-db-ja.com

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

4
Bax

ここで環境変数を悪用していると思います。そのスペースは変数の一部であり、$を使用してパラメーター拡張を実行しているときは、そのスペースをコマンドに渡すだけです。 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

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

5
slhck

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

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

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

mvn ${=SKIP}

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

setopt shwordsplit
mvn $SKIP

...しかし、これは不愉快な副作用をもたらす可能性があります。たとえば、単一の引数だと思っていたものを予期せず分割したり、ファイル名のワイルドカードのように見えるものをファイル名のリストに展開したりします。シェルワード分割はバグを引き起こす傾向があるため、zshではデフォルトで無効になっています。変数に複数の文字列を格納する本当に適切な方法は、プレーンな変数の代わりに配列を使用することです:

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

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

8
Gordon Davisson

set -xのため、印刷された診断行に単一引用符が表示されます。 mvnに到達しないようにします。変数の内容(-Dskip1 -Dskip2)は、mvnでは変数を厳密に引用する必要がないため(POSIX準拠のシェルで行うため)、単一の引数としてzshになります。

問題は、zshが変数を展開する方法にあるようです。 $SKIPが引用符で囲まれていない場合( 他のシェルで発生するように )にWord分割を実行したいと思いますが、zshでは通常は発生しません。

${=SKIP}を使用して 置換後のWord分割をトリガー

例:

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