我有以下代码(最小示例)。
#!/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
有人能解释一下为什么上面的 echo 能够修改变量的内容吗?
答案1
echo
上面脚本中的内容是不是修改变量var
。这不可以。这里发生了什么:
- 第一个
echo
被调用,打印一行"# Foo Bar Baz Hello World \r"
并立即用后面的字符\r
(此处:空格)覆盖它。 var
被设定为''
。- 函数
fun
已创建。 fun
被调用,var
被设置为'something else'
- 现在到了关键点:因为
echo -n
不打印换行符,所以 的输出会覆盖上面尾随字符之后的字符,直到达到 的var
长度。var
由于这种行为,输入 ( "# 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
如果操作员真的想知道为什么函数可以修改主程序中的变量,那么这与作用域有关。默认情况下,所有变量的作用域都是全局的,并且可以在函数内和函数外写入和读取。换句话说,函数在与调用程序相同的 shell 中执行,而不是在子 shell 中执行。
但是,变量可以声明为函数的本地变量,在这种情况下,它们仅在创建它们的函数(及其调用的任何函数)中可见,并在退出时被销毁。
创建子 shell 时,变量将放置在环境中,并且不能在子 shell 内更改。如果它们被覆盖,那么是通过在该子 shell 中创建一个变量来覆盖的,该变量在退出时会丢失。
考虑:
#!/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<"
首先创建两个变量,看看它们是什么。接下来创建函数并将“其他内容”分配给全局变量,并创建一个包含“local”的局部变量。调用该函数。现在看一下变量,全局的已经设置了,但是本地的还没有。
作为对比,最后一部分首先从环境中回显变量,设置它并回显它。然而,从子 shell 返回时,更改会丢失:
$ ./X
><><
>something else<><
>something else<
>level<
>something else<