Bashのechoが独立変数の内容を変更するのはなぜですか?

Bashのechoが独立変数の内容を変更するのはなぜですか?

次のコードがあります(最小の例)。

#!/bin/bash                                                                                              
                                                                                                         
echo -ne "# Foo Bar Baz Hello World \r                           "                                       
var=''                                                                                                   
                                                                                                         
function fun {
  # some stuff happening in between                                                                                         
  var='something else'
}

fun

echo $var

驚くべきことに、出力は次のようになります。

$ /tmp/test.sh
                           something else 

上記のエコーが変数の内容を変更できる理由を誰かが説明できますか?

答え1

echo上記のスクリプトの内容はいいえ変数を変更しますvar。これは許可されません。ここで何が起こっていますか?

  1. 最初のものがecho呼び出され、1行が印刷され、"# Foo Bar Baz Hello World \r"すぐに次の文字\r(ここでは空白)で上書きされます。
  2. varに設定されています''
  3. 機能がfun作成されました。
  4. fun呼び出され、次のようvarに設定されます。'something else'
  5. 今重要な点があります。echo -n改行文字が印刷されないため、 の出力は長さvarに達するまで上記の末尾文字の後の文字を上書きします。var

この動作により、入力の3つの部分("# Foo Bar Baz Hello World"、および)" "の内容がすべて'something else'重なり合い、全体が混乱します。

このタングルの出力はスクリプトによって「通過」されます(そして不快な副作用を引き起こす可能性があります)。

より明確に説明するには:

#!/bin/bash

echo -ne "# Foo Bar Baz Hello World             \r  ANYTHING"
var=''

function fun {
  var=D
}

fun

echo $var

出力:

$ /tmp/test.sh
  ANYTHINGDaz Hello World    

その結果、私たちは次のことを学びました。

echo -ne "Something \r"echo後にスペースがあり、スクリプト出力が切り捨てられた場合は戻り値と一緒に使用できますが、\r前の文字\r列の長さが次の値より小さい場合にのみ可能ですvar

=>避けてください。

答え2

オペレーターが関数が基本プログラムの変数を変更できる理由を本当に知りたい場合は、スコープの指定に関連しています。デフォルトでは、すべての変数の範囲はグローバルであり、関数の内側と外側の両方で書いて読み取ることができます。つまり、関数はサブシェルではなく呼び出し側プログラムと同じシェルで実行されます。

ただし、変数は関数に対してローカルに宣言できます。その場合、変数はその変数を生成した関数(および呼び出すすべての関数)内でのみ表示され、終了時に破棄されます。

サブシェルが作成されると、変数は環境に配置され、サブシェル内では変更できません。上書きすると、サブシェルに変数を作成して完了し、終了時にこの変数は失われます。

考慮する:

#!/bin/bash                                                                                              

var=''                                                                                                   
var2=''                                                                                                   
echo ">$var<>$var2<"
                                                                                                         
function fun {
  # some stuff happening in between                                                                                         
  var='something else'
  local var2='local'
}

fun
echo ">$var<>$var2<"

(
  echo ">$var<"
  var='level'
  echo ">$var<"
)
echo ">$var<"

まず、2つの変数を作成し、それが何であるかを確認します。次に、関数を作成し、「something else」をグローバル変数に割り当て、「local」を含むローカル変数を作成します。この関数を呼び出してください。これでグローバル変数は設定されていますが、ローカル変数は設定されていません。

比較のために、最後の部分はまず環境の変数をエコーし​​、設定してからエコーします。ただし、サブシェルから戻ると変更は失われます。

$ ./X
><><
>something else<><
>something else<
>level<
>something else<

関連情報