原因 @
zhlxsh #1 和 @
foam #2 说了,解决方法 @
foam #2 说了,我来扩展下思路
在通过『|』『()』『$()或``』启动的 subshell 中修改变量,只会在 subshell 中生效,不会影响 parent shell:
```bash
declare -i total=0
sum() {
for i in {1..3}; do
total+=i
done
printf '%4s: %d\n' "$1" "$total"
}
sum '|' | cat
(sum '()')
echo "$(sum '$()')"
echo "外部: $total"
```
结果,三种方式启动的 subshell ,都计算得 total=1+2+3=6 ,但实际都未修改外部的 total:
```
|: 6
(): 6
$(): 6
外部: 0
```
若要修改,就要在同一个 shell 环境中。对于『|』,可以尽量用『<<<』『< <()』等代替:
```bash
# seq 3 |
while read -r i; do
total+=i
done < <(seq 3)
```
如果要捕捉输出,就想办法赋值到某个变量中(如 @
foam #2 利用的 printf -v )。但归根结底,还是利用了 bash 的『动态作用域』。
bash 手册说,变量对自身及调用的子函数可见
> variables are visible only to the function and the commands it invokes
函数中使用了某个变量,先在自身找,找不到则在上一层调用者中找,一直到全局作用域
> visible variables and their values are a result of the sequence of function calls that caused execution to reach the current function. The value of a variable that a function sees depends on its value within its caller, if any, whether that caller is the "global" scope or another shell function
1. 所以,简单地,可以直接约定,子函数输出到 out 变量中,调用者直接用 out 变量
```bash
count=1
counter() {
(( count++ ))
out='test' # 自身找不到 out ,就在调用者 main 中找到 out 再赋值
}
main() {
local out
counter
echo "count: $count, out: $out"
}
main # 输出:count: 2, out: test
```
2. 将『要赋值到哪个变量』作为参数 /环境变量,传递给子函数,子函数自己想办法赋值
2.1 使用 @
foam #2 说的 printf -v
2.2 使用『引用』
```bash
count=1
global_out=
counter1() {
# 本函数内,out 是『名为「$1 的值」的变量』的引用(可同名,外部作用域的同名变量暂时被隐藏)
# 如,out 是 main_out 的引用。对 out 的操作,实际是对 main_out 操作(自身找不到 main_out ,就在 main 找)
declare -n out=$1; shift
(( count++ ))
out='test1'
}
counter2() {
# 本函数内,out 是『名为「$out 的值」的变量』的引用
# 右边的 out 是调用者临时扩充的环境变量,如 global_out (自身、main 找不到 global_out ,就在全局作用域找)
declare -n out=$out
(( count++ ))
out='test2'
}
main() {
local main_out
counter1 main_out # 作为参数传递
out=global_out counter2 # 作为临时环境变量传递(综合觉得这种调用好看)
echo "count: $count, main_out: $main_out, global_out: $global_out"
}
main # 输出:count: 3, main_out: test1, global_out: test2
```