シェルスクリプトに変数を設定するための3つの構文があるのはなぜですか?

シェルスクリプトに変数を設定するための3つの構文があるのはなぜですか?
root@ubuntu:~# echo ${x=1}
1
root@ubuntu:~# echo ${x:=1}
1
root@ubuntu:~# echo ${x-1}
1
root@ubuntu:~# 

シェルスクリプトに変数を設定するための3つの構文があるのはなぜですか?

3つの構文を使用すると、どのような技術的な利点がありますか?

プログラミング言語にも複数の言語はありません。

答え1

彼らは少し違うことをします。実際、最後の項目はecho ${x-1}実際には設定されていません。Xただし、式の値は1の場合にのみ置き換えられます。X設定されていません。

x=1一方、設定X無条件。

:= 演算子の場合。これはから来たものですケシ手動:

   ${parameter:=word}
          If  parameter is not set or is null then set it to word; the
          value of the parameter is then substituted.  Positional  parameters may 
          not be assigned to in this way.

Rubyでは||=

この家族を見るとXもっと = 演算子がありますが、:-これが私が見たものの中で最も人気のあるものです。それ代替変数がまだ設定されていない場合は、デフォルト値に設定します。したがって、次のように使用できます。

x=${1:-10}

英語は次のとおりです。 $ 1を設定しないと、10が割り当てられます。X、それ以外の場合は次に割り当てられますX$1の価値。関数には、パラメータにデフォルト値を割り当てる効果があります。だから

f() {
  typeset x
  x=${1-10}
  ...
}

Pythonと同じ:

def f(x=10): 

私の考えでは、David Kornは通信事業者の多様性が多少大きかったかもしれないことを認めます。POSIX規格、セクション2.6.2だからおそらくそこに滞在します。

プログラミング言語にも複数の言語はありません。

上記のように、これは誤解があります。正確に同じ。プログラミング言語では、代入文のバリエーションはまれではありません。私は||=それをRubyで言及しました。多くの言語にこの機能があります+=-=Perlで$ xを実行する前に定義しないと1に$x += 1設定されます$x(そして「厳密な」チェックを有効にしないと本当に残念です)。

答え2

私は、「変数割り当てのためのシェル構文がなぜそんなに多くなのか」という質問に対する唯一の答えだと思います。 「彼らはみんな少しずつ違う振る舞いをするからです」

シェルプログラミング言語では、$x次のいずれかを実行します${x}次に展開名前付きシェル変数の値ですx。つまり、シェルを含む行を解釈するときに変数の値に$x置き換えられます。$x

$ x=me; filepath=/home/$x
$ echo $filepath
/home/me

さらに、シェルは副作用のある拡張を含むさまざまな条件付き拡張を実装します。

条件付き職業

交換する前に条件付きで割り当て:

  • ${x=1}x以前に設定していない場合は1に設定します。
  • ${x:=1}x以前に設定されていない場合は1に設定するか、空の文字列に設定します。

例:

$ unset x
$ echo Hello, "${x=world}"
Hello, world
$ echo "$x"
world

$ # This sets x to the empty string
$ x=
$ echo Hello, "${x=world}"
Hello,
$ echo "$x"

$ echo Hello, "${x:=world}"
Hello, world
$ echo "$x"
world

条件付き拡張

拡張は変数またはいくつかの固定文字列の値です。変数はいいえ変更されました。

  • ${x-1}設定されている${x}場合に展開します。xそれ以外の場合に展開します1
  • ${x:-1}${x}設定されていて空でない場合は、x次に展開されます。それ以外の場合に展開します1
  • ${x+1}設定されている1場合に展開します。xそれ以外の場合は空の文字列に展開されます。
  • ${x:+1}1設定されていて空でない場合は、x次に展開されます。それ以外の場合は空の文字列に展開されます。

例:

$ unset x
$ echo Hello, "${x-world}"
Hello, world
$ echo "$x"

$ # This sets x to the empty string
$ x=
$ echo Hello, "${x-world}"
Hello,
$ echo Hello, "${x:-world}"
Hello, world
$ echo "$x"

条件エラー

  • ${x?error message}x設定しないと失敗し、エラーメッセージが出力されます。
  • ${x:?error message}x設定しないか空の文字列に設定すると失敗し、エラーメッセージが表示されます。

失敗すると、拡張が発生したコマンドは実行されません。シェルが対話型でない場合、シェル自体は終了します。

コロン

:すべての条件付き構文でaを使用すると、設定されていない変数が空の文字列に設定された変数と同じように動作することがわかります。時には変数の設定を解除するのは簡単ではないので、これは一般的に良い考えです。たとえば、上記のローカル割り当て構文の空の文字列にサブ変数を割り当てるのは簡単ですが、サブ環境から変数を削除するのは簡単ではありません。

$ export foo=Goodbye
$ foo= dash -c 'echo "${foo-Hello}", world'
, world
$ foo= dash -c 'echo "${foo:-Hello}", world'
Hello, world
$ echo "$foo"
Goodbye

コマンドによる条件付き拡張

時には、コマンドで実際に使用するのではなく、条件付き拡張を実行して副作用を排除するのが便利な場合があります。技術的にはこれは不可能ですが、何もしない特別な組み込みコマンドを使用できます:

# Equivalent to:
#  if [ -z "$x" ]; then x="default value"; fi
: "${x:=default value}"
# Equivalent to:
#   if [ $# -lt 3 ]; then
#     echo "Three arguments are required" 1>&2
#     exit 1
#   fi
: "${3?Three arguments are required.}" 

変数の割り当て

シェルは、スカラー変数に値を割り当てるための簡単な構文を提供します。

var=value

コマンドに割り当てのみが含まれている場合、割り当てはシェル環境の一部になります。ただし、割り当てがユーティリティ/スクリプト名の前にあるコマンドラインの一部である場合、割り当ては次に適用されます。子供子プロセスが使用する環境は次のとおりです。

$ foo=Hello; echo "$foo, world"
Hello, world
$ echo "$foo"
Hello
$ unset foo
$ foo=Hello echo "$foo, world"
, world
$ echo "$foo"

foo=Hello echo "$foo, world", the assignment is performed in the environment passed toecho . However, the argument"$foo, world" is evaluated in the main shell environment, in which$foo has no value. Furthermore, when the child process (echo , in this case) terminates, its environment is lost, so the assignment was useless. Contrast that with the case where the child process actually uses the variable: (dash bash` コマンドis a Posix-compatible shell; I'm just using it here for clarity.でも同じように動作します。 )

$ unset foo
$ foo=Hello dash -c 'echo "$foo, world"'
Hello, world
$ echo "$foo"

拡張割り当て構文

一部のシェル(bash、ksh、zshなど)も構文を提供します。

var+=value

これによりvalue変数が増加します。 「追加」の意味は変数の種類によって異なりますvar。一般文字列変数の場合はvalue末尾に追加されvar、整数で宣言された変数の場合はvalue数学的に追加されます。 ksh、zsh、および一部のbashバージョンでは、この割り当て構文をコマンドの一部としてサブ割り当てに使用できます。変数を数値として宣言する方法の詳細については、個々のシェルのドキュメントを参照してください。 (これはいいえ単に変数に数値を割り当てるだけで十分です。 )

これらのシェルには、次の構文を使用して割り当てられ、追加される配列変数もあります。

array=(value1 value2 ...)
array+=(value3 ...)

上記の割り当ての括弧は文法的です。左括弧を割り当てマークアップの一部として考えることができます。配列を子環境にエクスポートできないため、配列割り当ては単純なコマンドの一部として使用できません。

一部のシェルには、インデックスの割り当てをサポートする連想配列があります。宣言方法を含む連想配列の詳細については、個々のシェルのドキュメントを参照してください。

算術評価の課題

=算術評価では、Cに似た代入演算子(、など)を使用できますが、*=これは算術代入のみを実行します。+=Posixシェルには、算術拡張構文$((...))for ((expr; expr; expr))複合文の2つの算術評価コンテキストのみがあります。 (:上記の特殊な組み込み関数を使用して、算術拡張を文に変換できます。)

ただし、ほとんどのシェルは条件文を受け入れ、配列の添字と整数で宣言された変数への割り当てなど、数字を必要((...))とするコンテキストで算術評価も使用します。詳細については、シェルのマニュアルを参照してください。

変数拡張が整数ではなく算術式に再解釈される算術評価コンテキストには注意が必要です。一部のシェル(bashなど)では、注入攻撃を介して悪用する可能性があります。最善の方法は、算術評価コンテキストで信頼できない変数を使用しないことです。

関連情報