web-dev-qa-db-ja.com

空の引用符付き変数をどのようにして何にも拡張できませんか?

スクリプトを実行しているとしましょう:

some-command "$var1" "$var2" ...

そして、var1は空です。空の文字列の代わりに何も置換しないほうがよいため、実行するコマンドは次のようになります。

some-command "$var2" ...

ではなく:

some-command '' "$var2" ...

変数をテストして条件付きで含めるよりも簡単な方法はありますか?

if [ -n "$1" ]; then
    some-command "$var1" "$var2" ...
    # or some variant using arrays to build the command
    # args+=("$var1")
else
    some-command "$var2" ...
fi

Bash、zshなどで何も拡張できないパラメータ置換はありますか?残りの引数でまだグロビングを使用したい場合があるので、それを無効にして変数の引用符を外さないでください。

23
muru

Posix準拠のシェル および Bashの所有者${parameter:+Word}

parameterが設定されていないかnullの場合、nullに置き換えられます。それ以外の場合、Wordの展開(またはWordの場合は空の文字列)省略)で代用します。

だからあなたはただ行うことができます:

${var1:+"$var1"}

およびvar1をチェックし、"$var1"が設定されており、空ではない場合(通常の二重引用符で囲まれている場合)に使用します。それ以外の場合は、何も展開しません。 innerの部分のみがここで引用されており、全体ではないことに注意してください。

同じことがzshでも機能します。変数を繰り返す必要があるため、理想的ではありませんが、望みどおりに機能します。

Set-but-empty変数を空の引数に展開する場合は、代わりに${var1+"$var1"}を使用します。

31
Michael Homer

引用符を省略した場合、デフォルトでzshが実行します。

_some-command $var1 $var2
_

実際、パラメーター展開の周りでzshでまだ引用符が必要な唯一の理由は、zsh他のシェルに影響する他の問題が発生する他の問題)があるため、その動作(空の削除)を回避するためです'パラメータ展開を引用しない(暗黙のsplit + glob)

Splitとglobを無効にすると、他のPOSIXのようなシェルでも同じことができます。

_(IFS=; set -o noglob; some-command $var1 $var2)
_

さて、変数が0または1の値を持つことができる場合、それは配列であり、スカラー変数ではなく、次のように使用する必要があると主張します。

_some-command "${var1[@]}" "${var2[@]}"
_

そして、_var1_に1つの値を含める場合はvar1=(value)を、空の値を1つ含める場合はvar1=('')を、含める場合はvar1=()を使用します- no値。

5

私は、-nの有無にかかわらずコマンドを開始するbashスクリプトでrsyncを使用してこれに遭遇し、ドライランを切り替えました。 rsyncと多くのgnuコマンドは、有効な最初の引数として''を取り、そこにない場合とは異なる動作をすることがわかりました。

Nullパラメータはほとんど完全に見えないため、デバッグにはかなり時間がかかりました。

Rsyncリストの誰かがこの問題を回避する方法を教えてくれた一方で、(かなり)コーディングを簡略化しました。私がそれを正しく理解していれば、これは@StéphaneChazelasの最後の提案のバリエーションです。

コマンド引数をいくつかの個別の変数に作成します。これらは、問題に適した任意の順序またはロジックで設定できます。

次に、最後に、変数を使用してすべてを適切な場所に配置した配列を作成し、それを実際のコマンドの引数として使用します。

このように、コマンドは、引数のバリエーションごとに繰り返されるのではなく、コード内の1つの場所でのみ発行されます。

空の変数は、このメソッドを使用すると消えます。

私はevalの使用が非常に嫌われていることを知っています。すべての詳細を覚えているわけではありませんが、このように動作させるためには、埋め込みが必要な空白を含むパラメーターの処理に必要なようです。

例:

dry_run=''
if [[ it is a test run ]]
then
  dry_run='-n'
fi
...
rsync_options=(
  ${dry_run}
  -avushi
  ${delete}
  ${excludes}
  --stats
  --progress
)
...
eval rsync "${rsync_options[@]}" ...
0
Joe