如何在Bash函数输出被管道传输时设置全局变量?
如何在Bash函数输出被管道传输时设置全局变量?
嘿,这个问题其实是Bash里管道的一个经典“坑”——管道会给每个命令创建独立的子shell进程!
咱们先搞清楚为啥两种情况结果不一样:
- 当你直接运行
testit ; echo $a时,testit函数是在你当前的shell进程里执行的,所以它设置的变量a是全局的,当前shell自然能读到这个值。 - 但一旦用了管道
testit | cat,Bash会给管道左边的testit单独开一个子shell进程,函数在这个子进程里跑,设置的a变量只属于这个子进程。等子进程执行完退出,这个变量就跟着消失了,你原本的父shell(就是你敲命令的那个shell)根本看不到它。
那怎么解决这个问题呢?给你几个实用的办法:
方法1:用进程替换代替管道
进程替换不会让左边的命令跑在子shell里(大多数默认的Bash配置都支持这个),这样函数就在当前shell执行,变量能保留下来:
testit > >(cat) ; echo $a
运行这个命令,你会发现既输出了Hello world,echo $a也能打印出4。
方法2:把函数输出存到文件再处理
先让函数在当前shell执行,把输出写到临时文件,之后再用cat处理这个文件:
# 执行函数,输出存到temp.txt testit > temp.txt # 处理输出 cat temp.txt # 此时变量a已经被设置 echo $a # 用完可以删掉临时文件 rm temp.txt
这个方法简单直观,适合不需要实时处理输出的场景。
方法3:让函数把变量值输出到标准错误(stderr)
修改函数,把要传递的变量值输出到stderr,然后在父shell里捕获这个值:
function testit() { a=4 echo "Hello world" # 正常输出到stdout echo "$a" >&2 # 把变量值输出到stderr }
然后你可以这样用:
# 用管道处理stdout的内容 testit | cat # 捕获stderr的内容赋值给变量a a=$(testit 2>&1 >/dev/null) # 现在就能拿到变量值了 echo $a
这个方法适合需要同时处理输出和传递变量的场景。
方法4:使用命名管道(FIFO)
创建一个命名管道,让函数把输出写到管道,同时让cat在后台读取管道,这样函数依然在当前shell执行:
# 创建命名管道 mkfifo mypipe # 后台启动cat读取管道内容 cat < mypipe & # 执行函数,输出写入管道(此时函数在当前shell执行) testit > mypipe # 查看变量a,已经被设置 echo $a # 清理命名管道 rm mypipe
核心思路就是:避免让函数在子shell里执行,因为子shell的变量无法传递回父shell。上面的几种方法都是围绕这个思路来的,你可以根据自己的场景选最合适的~
备注:内容来源于stack exchange,提问作者jicarretero
相关产品推荐
相关产品推荐

