「副作用」を理解したり、1つのコマンドに複数のコマンドが含まれていますか?

「副作用」を理解したり、1つのコマンドに複数のコマンドが含まれていますか?

この質問はPOSIX準拠のシェルスクリプトに焦点を当てています。

変数は通常、次のように増加します。

i=3
: $(( i += 1 ))
echo "return code = $?"        # return code = 0
echo "i = $i"                  # i = 4

このコマンドを$(( i += 1 ))「副作用」と呼びますか:

:( equals の内容を読みましたtrue:truefalsefalse

値が正常に増加しましたが、割り当てが$i機能しないために「副作用」が発生するのはなぜですか?

a='four'
: a='five'
echo "return code = $?"        # return code = 0
echo "a = $a"                  # a = four

時には、1つのコマンドで複数のコマンドを実行するスクリプトを見ることができます。ほとんどの場合、人々はこの構造を使用してIFSIFS='' read -r REPLYこれは「副作用」と同じ構造ですか?しかし、すべての副作用はa=6現在のシェル内で実際の影響を与えます。宿題はここで行われます。

a=6 b=7 c=8
echo "a = $a"                  # a = 6
echo "b = $b"                  # b = 7
echo "c = $c"                  # c = 8

この構造を「副作用」といいますか?彼の記録はどこにありますか?それともどこで学ぶことができますか?以下のPOSIXドキュメントにはこの構造がありません。

https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09

ベストアンサー1

これを:空のコマンドと呼びます。その文書は以下にありますman bash

:[論争]

効果はありません。コマンドは、パラメーターを拡張し、指定されたリダイレクトを実行する以外は何も行いません。戻り状態は 0 です。

またはbashシェルを実行している場合は、以下を使用してくださいhelp :

$ help :
:: :
    Null command.
    
    No effect; the command does nothing.
    
    Exit Status:
    Always succeeds.

POSIX仕様にあります。ここ:

名前

コロン - nullユーティリティの要約:[パラメータ...]

説明する

このユーティリティはコマンドパラメータのみを拡張できます。 if コマンドの then 条件など、コマンドが必要な場合に使用されますが、コマンドは何も行いません。

今あなたが言う副作用は上記の最初の文で説明された結果です。 「コマンドは何もしません。主張の拡大に加えて".:はコマンドなので、次の内容はすべてパラメータです。$(( ))これは"算術拡張"と呼ばれ、man bash次のように文書化されています。

算術拡張

算術拡張を使用すると、算術式を評価して結果を変更できます。

算術拡張の形式は次のとおりです。

$((expression))

式は二重引用符で囲まれたように処理されますが、括弧内の二重引用符は特に処理されません。式のすべてのトークンは、パラメータと変数の拡張、コマンドの置き換え、および引用符の削除を受けます。結果は評価する算術式として扱われます。算術拡張は入れ子にすることができます。

[。 。 .]

so は$(( i += 1 ))i1 つを追加して結果を返す」を意味します。しかし、これについては評価が必要であり、拡大する、これが一緒に使用されているのがわかる理由です::パラメータが拡張されるため、平均値: $(( i += 1 ))i1ずつ増加します。単独で実行しようとすると$(( i += 1 ))(最初の例では)最初に展開され、次にシェルがコマンド44実行を試みてエラーを返します。

$ $(( i += 1 ))
bash: 4: command not found

しかし、実行すると、: $(( i += 1 ))シェルは2つのことを行います。まず、算術拡張を適用し、次の値をi使用4します。: 4:

$ : 4
$ 

しかし、なぜもっと簡単で、POSIXの代わりにこれを行うのかわかりません。

$ i=3
$ i=$(( i + 1 ))
$ echo "$i"
4

または、POSIXシェルでは可能ですbashが、POSIXシェルではそうではありません。

$ i=3
$ (( i += 1 ))
$ echo "$i"
4

ここで次のポイントは、これがvariable=value command別の獣であり、別の方法で扱われることです。 POSIX仕様の関連部分は次のとおりです。2.9.1 簡単なコマンド、最も関連性の高い部分は次のとおりです。

「単純なコマンド」は、任意の順序で任意の変数割り当ておよびリダイレクトのシーケンスであり、任意にワードおよびリダイレクトが続き、制御演算子によって終了される。

与えられた単純なコマンドを実行する必要があるとき[。 .]では、コマンドテキストの先頭から最後まで、次の拡張、割り当て、およびリダイレクトを実行する必要があります。

  1. シェル構文規則に従って変数の割り当てまたはリダイレクトによって識別された単語は、ステップ3と4の処理のために保存されます。

  2. 変数の割り当てやリダイレクト以外の単語は拡張する必要があります。拡張後に残りのフィールドがある場合、最初のフィールドはコマンド名として扱われ、残りのフィールドはコマンドの引数です。

  3. リダイレクトは、リダイレクトで説明されているように実行する必要があります。

  4. チルダ拡張、パラメータ拡張、コマンド置換、算術拡張、および引用符の削除を割り当てる前に、すべての変数割り当てを拡張する必要があります。

そして:

変数の割り当ては、次のように実行する必要があります。

[。 。 .]

  • コマンド名が特殊な組み込みユーティリティまたは関数でない場合は、コマンドの実行環境エクスポート変数を[...]として指定する必要があります。

[。 。 .]

  • コマンド名が特殊な組み込みユーティリティの場合、変数の割り当ては現在の実行環境に影響します。 set -aオプションがオンになっていない場合は指定されません(セットを参照)。

  • 特別な組み込みユーティリティの実行中に変数がエクスポートした属性を取得するかどうか

  • 特別な組み込みユーティリティが完了した後でも、変数の割り当ての結果として得られるエクスポート属性はまだ存在しますか?

したがって、シェルがそれを実行するためにコマンドを読み取るとき、まずそれを分析して変数の割り当て(foo=bar)のように見えることを見つけて、関連する値を割り当てます。この割り当ては、「通常」コマンドの場合にのみそのコマンドに影響します。コマンドが実行される環境が機能する理由です。

$ foo=bar sh -c 'echo "foo is $foo"'
foo is bar
$ echo "foo is $foo"
foo is 

変数の割り当ては、sh -cコマンドの実行環境で機能し、存在しますが、コマンドを開始した親シェルには存在しません。これはシェルコマンドの処理の一部に過ぎず、シェルに影響を与えずに単一のコマンドに対してのみ変数を設定する方法です。

コマンドが特殊な組み込みコマンドの場合(たとえば、割り当てがコマンドの後に残っているかどうかは指定されません:。これは、完了後に変数を使用できるようにするvar=foo :いくつかのシェルがあることを意味しますが、これはPOSIXでは必須ではなく、他のシェルは異なる方法で動作します。$varcommand

$ cat foo.sh 
foo=bar :
echo "foo is $foo"
$ awk -F'/' '/^\//{print $NF}' /etc/shells | sort -u | 
  while read shell; do 
    echo "==== $shell ===="; 
    "$shell" foo.sh; 
done
==== bash ====
foo is 
==== dash ====
foo is bar
==== fish ====
foo is 
==== ksh ====
foo is bar
==== mksh ====
foo is bar
==== rbash ====
foo is 
==== sh ====
foo is bar
==== yash ====
foo is bar
==== zsh ====
foo is 

とにかく、これはすべてwhile IFS= read ...あなたが次のように見て構成する理由です。変数を混乱させたくないので、異なる$IFS値を持つために必要な特定のコマンドを実行するために変数を変更するだけです。

最後に、この方法が効果的な理由について話しましょう。

a=6 b=7 c=8

そこにはコマンドがなく、変数の割り当てだけがあるからです。したがって、これらの変数は実際には現在のシェルに割り当てられます。

おすすめ記事