次のコードがあります(最小の例)。
#!/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
。これは許可されません。ここで何が起こっていますか?
- 最初のものが
echo
呼び出され、1行が印刷され、"# Foo Bar Baz Hello World \r"
すぐに次の文字\r
(ここでは空白)で上書きされます。 var
に設定されています''
。- 機能が
fun
作成されました。 fun
呼び出され、次のようvar
に設定されます。'something else'
- 今重要な点があります。
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<