为何命令替换会改变函数的名称引用参数的行为?
为何命令替换会改变函数的名称引用参数的行为?
哎,这个问题我太熟了!本质原因其实是命令替换会启动子shell执行代码,而子shell和父shell的变量环境是完全隔离的。
咱们把两种场景拆开说就懂了:
- 当你直接运行
update_name_ref_and_echo foo时,函数是在当前的父shell环境里跑的。函数里的名称引用name_ref直接关联到父shell的foo变量,修改自然能直接生效,所以你能看到foo从“old value”变成“new value”。 - 但用
capture=$(update_name_ref_and_echo foo)这种命令替换的时候,bash会悄悄创建一个子shell,把函数放到这个子shell里执行。函数里的名称引用确实会在子shell里把foo改成“new value”,但这个修改只存在于子shell的私有环境中。等子shell执行完退出,所有的变量修改都会被直接丢弃,父shell里的foo当然还是原来的样子。
那如果想既要捕获函数的输出,又要让变量修改生效,可以绕开子shell的隔离,比如用文件描述符或者临时文件来传递输出:
#!/bin/bash function update_name_ref_and_echo () { local -n name_ref=$1 name_ref="new value" # 把输出写到自定义的文件描述符3,避开默认stdout的命令替换陷阱 echo "bar" >&3 } foo="old value" echo "foo before: $foo" # 先打开文件描述符3,指向临时文件 exec 3> /tmp/func_output.tmp # 直接在父shell执行函数,变量修改会直接生效 update_name_ref_and_echo foo # 关闭文件描述符 exec 3>&- # 从临时文件读取捕获的输出 capture=$(cat /tmp/func_output.tmp) rm /tmp/func_output.tmp echo "foo after: $foo" echo "captured content: $capture"
这样运行的话,既能成功修改父shell的foo变量,又能拿到函数的输出内容。
备注:内容来源于stack exchange,提问作者Ding Liu
相关产品推荐
相关产品推荐

